One place for hosting & domains

      Redux

      Build A Media Library with React, Redux, and Redux-saga – Part 1

      Introduction

      Building applications with React can be overwhelming even after you’ve understood the elegant philosophy behind it. More so, managing large applications with React can be confusing at first. The ecosystem has grown with great libraries to save you some nightmares. But that also makes it difficult at first to figure out which library to use.

      In this two-part tutorial, we’ll build and deploy a media library app. The application allows users to search and display images and short videos from external services (Flickr and Shutterstock). It would also allow users to select images and videos for preview.

      We will build this application with:

      We will be using Yahoo’s Flickr API and ShutterStock API for images and short videos respectively.

      This tutorial assumes you have a basic understanding of JavaScript and React. Don’t worry if you have none. We will walk through and build the application from the ground up.

      Part 1 of this tutorial would cover basic React setup with create-react-app package, organizing our project workflow, defining routes, and of course testing it out.

      In Part 2, we will be using Redux and its async libraries; we will set it up and then integrate it into our application. Finally, we will deploy our application to Heroku for sharing with our friends. Our application would look thus when we’re done.

      Our app will be structured to allow you to either contribute to it or use it as a sample boilerplate for bootstrapping your React/Redux applications.

      There are loads of React boilerplate out there to help you get started with React. But we’ll be using create-react-app authored by the Facebook team. It allows you to create React applications with no configuration. create-react-app provides developers with the benefits of a more complex setup out of the box.

      Let’s get started…

      First, install the package globally:

      1. npm install -g create-react-app

      Then, create the media-library application:

      1. create-react-app media-library

      Bam. Our React basic setup is complete with scripts to start, build, and eject. Take a look at your package.json.

      Let’s test it out.

      1. cd media-library
      2. npm start

      Now, we can structure our project directory and add other dependencies.

      1. npm install --save redux redux-saga [email protected] react-redux

      Then, remove the default sample app:

      1. rm -rf src/**

      Project Directory

      Media-library
          - public
              - favicon.ico
              - index.html
          - src
              - Api
                  - api.js
              - actions
                  - mediaActions.js
              - common
                  - Header.js
              - components
                  - HomePage.js
                  - PhotoPage.js
                  - VideoPage.js
              - constants
                  - actionTypes.js
              - containers
                  - App.js
                  - MediaGalleryPage.js
              - reducers
                  - imageReducer.js
                  - index.js
                  - initialState.js
                  - videoReducer.js
              - sagas
                  - mediaSaga.js
                  - index.js
                  - watcher.js
              - styles
                  - style.css
              - store
                  - configureStore.js
              - routes.js
              - index.js
          - package.json
      

      If the project directory looks verbose, just be patient, and let’s walk-through. The intent of the project structure is to allow you to extend the application’s functionality beyond this tutorial. This would help you stay organized moving forward.

      What the heck is happening up there?

      1. View layer is our React component. It makes a request for action based on interactions on/with the application.
      2. Action, commonly called action creator returns a formatted object of the action type and optional payload which is then dispatched to the store.
      3. Redux-saga is a Redux middleware for handling async operations like fetching photos and videos from our API. It makes asynchronous operations look like standard JavaScript synchronous code making it easy to read, test, and reason.
      4. APIs are resource locations to fetch photos and videos in our own case.
      5. Reducers are simply pure functions whose purpose in life is to accept the state tree and an action from the store; make a copy of the previous state, transform it and then return a new state to the store.
      6. Store is a single object that holds the complete state of your application. It delegates the reducer with the responsibility of changing state when an action is dispatched.

      When the store receives an updated state, it transmits to the view layer to be rerendered.

      Now that we understand the workflow, let’s dive into coding.

      common/Header.js

      import React from 'react';
      import { Link, IndexLink } from 'react-router';
      
      const Header = () => (
        <div className="text-center">
          <nav className="navbar navbar-default">
            <IndexLink to="/" activeClassName="active">Home</IndexLink>
            {" | "}
            <Link to="library" activeClassName="active">Library</Link>
          </nav>
        </div>
      );
      
      export default Header;
      

      Link allows you to navigate to different routes in your application.

      IndexLink is the same as Link with the exception of OnlyActiveOnIndex prop set on it.

      components/HomePage.js

      import React from 'react';
      import { Link } from 'react-router';
      
      
      const HomePage = () => (
        <div className="jumbotron center">
          <h1 className="lead">Welcome to Media Library built with React, Redux, and Redux-saga </h1>
          <div>
            <Link to="library">
              <button className="btn btn-lg btn-primary"> Visit Library</button>
            </Link>
          </div>
        </div>
      );
      
      export default HomePage;
      

      containers/App.js

      import React, { Component, PropTypes } from 'react';
      import Header from '../common/Header';
      
      
      
      class App extends Component {
        render() {
          return (
            <div className="container-fluid text-center">
              <Header />
              {this.props.children}
            </div>
          );
        }
      }
      App.propTypes = {
        children: PropTypes.object.isRequired
      };
      
      export default App;
      

      App component is the parent component of our app. Every other component is a child to it. this.props.children is where other child components are rendered.

      We will implement the library route and the component that maps to it in Part 2 of this tutorial.

      You would notice that for Header and HomePage components, we’re using stateless functional component. This approach allows us to separate our presentational components from the container components.

      It’s a good practice as it enforces functional composition and component reusability.
      Whereas container components are responsible for your business logic and connecting with the store, presentational components are responsible for the look of your view.

      Simply put, presentational components are components whose purpose in life is to render values to the DOM. Container components also known as smart components provide props and behavior to presentational components.

      Let’s wire up our project routes.

      routes.js

      import React from 'react';
      import { Route, IndexRoute } from 'react-router';
      import App from './containers/App';
      import HomePage from './components/HomePage';
      
      
      
      
      
      export default (
        <Route path="/" component={App}>
          <IndexRoute component={HomePage} />
        </Route>
      );
      

      Now let’s add the entrance to our application – index.js.

      import React from 'react';
      import ReactDOM from 'react-dom';
      import { Router, browserHistory } from 'react-router';
      import routes from './routes';
      
      
      ReactDOM.render(
          <Router history={browserHistory} routes={routes} />,
          document.getElementById('root')
      );
      

      We pass in our routes and browserHistory as props to Router here.
      browserHistory uses your browser’s History API to create a clean and real URL without the gibberish that comes with using hashHistory. hashHistory has its use case, though.

      Router is a high-level API that keeps your UI and URL in sync. It ensures that required props are passed whenever you change URL.

      ReactDOM is the API for mounting our application on the DOM node(root, in our own case).

      Two more things before we test our app.

      Add a bootstrap link to a CDN in our public/index.html.

      <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
      

      Let’s add some custom styling.

      styles/style.css

      body {
        margin: 0;
        padding: 0;
        font-family: Helvetica, Arial, Sans-Serif, sans-serif;
        background: white;
      }
      
      .title {
        padding: 2px;
        text-overflow-ellipsis: overflow;
        overflow: hidden;
        display: block;
      }
      
      .selected-image, .select-video {
        height: 500px;
      }
      
      .selected-image img, .select-video video {
        width: 100%;
        height: 450px;
      }
      
      .image-thumbnail, .video-thumbnail {
        display: flex;
        justify-content: space-around;
        overflow: auto;
        overflow-y: hidden;
      }
      
      .image-thumbnail img, .video-thumbnail video {
        width: 70px;
        height: 70px;
        padding: 1px;
        border: 1px solid grey;
      }
      

      Let’s test our app now…

      1. npm start

      Navigate to http://localhost:3000 on your browser.

      Bam!!! We’re up again

      Building application with React gets better as you understand the flow. In this part, we did:

      1. Set up configurations.
      2. Set up the project structure.
      3. Complete Header, Homepage, and App (parent) components.
      4. Map routes to different components.
      5. What else? We tested our app.

      In the second part of this tutorial, we will be exploring the power of Redux, Redux-saga, and separating our state management system from the React components for scalability and maintainability.

      Redux Thunkを使用した非同期Reduxアクションを理解する


      はじめに

      デフォルトでは、Reduxのアクションは同期的にディスパッチされます。これは、外部APIと通信したり副作用を実行する必要がある重要なアプリにとっては問題です。Reduxでは、ディスパッチされるアクションとレデューサーに到達するアクションの間に位置するミドルウェアも使用できます。

      副作用と非同期アクションを可能にする、非常に人気のあるミドルウェアライブラリが2つあります。Redux ThunkとRedux Sagaです。この記事では、Redux Thunkについて説明します。

      Thunkは、関数を使用して操作の評価/計算を遅らせるプログラミングの概念です。

      Redux Thunkは、アクションオブジェクトの代わりに関数を返すアクションクリエーターを呼び出すことができるミドルウェアです。この関数はストアのディスパッチメソッドを受け取り、非同期操作が完了すると、関数本体内で通常の同期アクションをディスパッチするために使用されます。

      この記事では、Redux Thunkを追加する方法と、仮のTodoアプリケーションにどのように適合するかを学びます。

      前提条件

      この記事は、ReactとReduxの基本的な知識があることを前提としています。Reduxを使い始めようという場合は、この記事を参照してください。

      このチュートリアルは、実行すべきタスクと、完了したタスクを追跡する仮のTodoアプリケーションから構築されています。 新しいReactアプリケーションの生成にcreate-react-appが使用され、reduxreact-reduxaxiosがすでにインストールされていることを前提としています。

      Todoアプリケーションを最初から構築する方法の詳細については、ここでは説明しません。これは、Redux Thunkの機能を強調するための概念設定として示されています。

      redux thunkを追加する

      まず、端末を使用して、プロジェクトディレクトリに移動 し、プロジェクトにredux-thunkパッケージをインストールします。

      • npm install redux-thunk@2.3.0

      :Redux Thunkは、わずか14行のコードです。Reduxミドルウェアが内部でどのように機能するかについては、こちらのソースを確認してください。

      ここで、ReduxのapplyMiddlewareを使用してアプリのストアを作成するときに、ミドルウェアを適用します。reduxreact-reduxを使用したReactアプリケーションを前提とすると、index.jsファイルは次のようになります。

      src/index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import { Provider } from 'react-redux';
      import { createStore, applyMiddleware } from 'redux';
      import thunk from 'redux-thunk';
      import './index.css';
      import rootReducer from './reducers';
      import App from './App';
      import * as serviceWorker from './serviceWorker';
      
      // use applyMiddleware to add the thunk middleware to the store
      const store = createStore(rootReducer, applyMiddleware(thunk));
      
      ReactDOM.render(
        <Provider store={store}>
          <App />
        </Provider>,
        document.getElementById('root')
      );
      

      これで、Redux Thunkはアプリケーションにインポートされ、アプリケーションに適用されます。

      サンプルアプリケーションでRedux Thunkを使用する

      Redux Thunkの最も一般的な使用例は、外部APIと非同期で通信してデータを取得または保存することです。Redux Thunkを使用すると、リクエストのライフサイクルに従ったアクションを外部APIに簡単にディスパッチできます。

      通常、新しいTodo項目を作成するには、最初にアクションをディスパッチして、Todo項目の作成が開始されたことを示します。次に、Todoアイテムが正常に作成され、外部サーバーから返された場合は、新しいTodo項目を使用して別のアクションをディスパッチします。エラーが発生し、 Todoがサーバーに保存されない場合は、代わりにエラーが発生したアクションがディスパッチされます。

      Redux Thunkを使用して、これがどのように実行されるかを確認しましょう。

      コンテナコンポーネントで、アクションをインポートして、ディスパッチします。

      src/containers/AddTodo.js

      import { connect } from 'react-redux';
      import { addTodo } from '../actions';
      import NewTodo from '../components/NewTodo';
      
      const mapDispatchToProps = dispatch => {
        return {
          onAddTodo: todo => {
            dispatch(addTodo(todo));
          }
        };
      };
      
      export default connect(
        null,
        mapDispatchToProps
      )(NewTodo);
      

      このアクションは、Axiosを使用してJSONPlaceholder(https://jsonplaceholder.typicode.com/todos)のエンドポイントにPOSTリクエストを送信します。

      src/actions/index.js

      import {
        ADD_TODO_SUCCESS,
        ADD_TODO_FAILURE,
        ADD_TODO_STARTED,
        DELETE_TODO
      } from './types';
      
      import axios from 'axios';
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(`https://jsonplaceholder.typicode.com/todos`, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              dispatch(addTodoSuccess(res.data));
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      const addTodoSuccess = todo => ({
        type: ADD_TODO_SUCCESS,
        payload: {
          ...todo
        }
      });
      
      const addTodoStarted = () => ({
        type: ADD_TODO_STARTED
      });
      
      const addTodoFailure = error => ({
        type: ADD_TODO_FAILURE,
        payload: {
          error
        }
      });
      

      addTodoアクションクリエーターが、通常のアクションオブジェクトの代わりに関数を返す方法に注目してください。この関数は、ストアからディスパッチメソッドを受け取ります。

      関数本体の内部で、最初にストアに 即時同期アクションをディスパッチして、外部APIを使用してTodoの保存を開始したことを示します。次に、Axiosを使用して、サーバーに実際のPOSTリクエストを行います。サーバーからの正常な応答の場合は、応答から受信したデータを使用して同期成功アクションをディスパッチしますが、失敗応答の場合は、エラーメッセージを使用して別の同期アクションをディスパッチします。

      この場合のJSONPlaceholderのような外部APIを使用すると、実際のネットワーク遅延が発生していることが確認できます。ただし、ローカルバックエンドサーバーを使用している場合は、ネットワーク応答が速すぎて実際のユーザーが経験しているネットワーク遅延を経験できない可能性があるため、開発時に人工的な遅延を追加することができます。

      src/actions/index.js

      // ...
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(ENDPOINT, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              setTimeout(() => {
                dispatch(addTodoSuccess(res.data));
              }, 2500);
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      // ...
      

      エラーシナリオをテストするには、手動でエラーをスローします。

      src/actions/index.js

      // ...
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(ENDPOINT, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              throw new Error('addToDo error!');
              // dispatch(addTodoSuccess(res.data));
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      // ...
      

      完全を期すために、Todoレデューサーがどのようにリクエストのライフサイクル全体を処理するかを次の例で示します。

      src/reducers/todosReducer.js

      import {
        ADD_TODO_SUCCESS,
        ADD_TODO_FAILURE,
        ADD_TODO_STARTED,
        DELETE_TODO
      } from '../actions/types';
      
      const initialState = {
        loading: false,
        todos: [],
        error: null
      };
      
      export default function todosReducer(state = initialState, action) {
        switch (action.type) {
          case ADD_TODO_STARTED:
            return {
              ...state,
              loading: true
            };
          case ADD_TODO_SUCCESS:
            return {
              ...state,
              loading: false,
              error: null,
              todos: [...state.todos, action.payload]
            };
          case ADD_TODO_FAILURE:
            return {
              ...state,
              loading: false,
              error: action.payload.error
            };
          default:
            return state;
        }
      }
      

      getStateの説明

      ディスパッチメソッドを状態から受信することに加えて、Redux Thunkを使用して非同期アクションクリエーターから返される関数は、ストアのgetStateメソッドも受信するため、現在のストア値を読み取ることができます。

      src/actions/index.js

      export const addTodo = ({ title, userId }) => {
        return (dispatch, getState) => {
          dispatch(addTodoStarted());
      
          console.log('current state:', getState());
      
          // ...
        };
      };
      

      上記の場合、現在の状態がコンソールに出力されます。

      例えば:

      {loading: true, todos: Array(1), error: null}
      

      getStateを使用すると、現在の状態に応じてさまざまな処理を行うことができます。たとえば、アプリを一度に4つのTodo項目のみに制限したい場合は、状態にTodo項目の最大数がすでに含まれている場合に、関数から戻ることができます。

      src/actions/index.js

      export const addTodo = ({ title, userId }) => {
        return (dispatch, getState) => {
          const { todos } = getState();
      
          if (todos.length > 4) return;
      
          dispatch(addTodoStarted());
      
          // ...
        };
      };
      

      上記では、アプリは4つのTodo項目に制限されます。

      まとめ

      このチュートリアルでは、Redux ThunkをReactアプリケーションに追加して、アクションを非同期にディスパッチできるようにする方法について見てきました。これはReduxストアを利用して、外部APIに依存している場合に役立ちます。

      React について詳しく知りたい場合は、How To Code in React.js(React.js のコーディング方法) シリーズを参照するか、演習とプログラミングプロジェクトの React トピックページをご覧ください。



      Source link

      Memahami Aksi Redux Asinkron dengan Redux Thunk


      Pengantar

      Secara asali, aksi Redux dikirimkan secara sinkron, yang menjadi masalah bagi aplikasi nontrivial yang perlu berkomunikasi dengan API eksternal atau melakukan efek samping. Redux juga memungkinkan pengiriman middleware yang berada di antara aksi dan aksi mencapai reducer.

      Ada dua pustaka middleware yang sangat populer yang memungkinkan efek samping dan akses asinkron: Redux Thunk dan Redux Saga. Dalam artikel ini, Anda akan mendalami Redux Thunk.

      Thunk adalah konsep pemrograman yang menggunakan fungsi untuk menunda evaluasi/kalkulasi suatu operasi.

      Redux Thunk adalah middleware yang memungkinkan Anda memanggil pembuat aksi yang mengembalikan fungsi sebagai ganti objek aksi. Fungsi itu menerima metode pengiriman penyimpanan, yang kemudian digunakan untuk mengirim aksi sinkron di dalam isi fungsi setelah operasi asinkron selesai.

      Dalam artikel ini, Anda akan mempelajari cara menambahkan Redux Thunk dan membuatnya cocok dengan aplikasi Todo hipotetis.

      Prasyarat

      Artikel ini beranggapan Anda telah memiliki sejumlah pengetahuan dasar tentang React dan Redux. Anda dapat merujuk artikel ini jika sudah mulai menggunakan Redux.

      Tutorial ini membangun dari aplikasi Todo hipotetis yang melacak berbagai tugas yang perlu dilakukan dan yang telah selesai. Kita dapat menganggap bahwa create-react-app telah digunakan untuk membuat aplikasi React baru, serta redux, react-redux, dan axios telah terinstal.

      Detail lebih akurat tentang cara membangun aplikasi Todo dari awal tidak dijelaskan di sini. Artikel ini disajikan sebagai pengaturan konseptual untuk menyoroti Redux Thunk.

      Menambahkan redux-thunk

      Pertama, gunakan terminal untuk menavigasi ke direktori proyek dan instal paket redux-thunk di proyek Anda:

      • npm install redux-thunk@2.3.0

      Catatan: Kode Redux Thunk hanya 14 baris. Lihatlah sumbernya di sini untuk mempelajari cara kerja middleware Redux di balik layar.

      Sekarang, terapkan middleware saat membuat penyimpanan aplikasi menggunakan applyMiddleware Redux. Dengan anggapan aplikasi React berisi redux dan react-redux, berkas index.js Anda mungkin terlihat seperti ini:

      src/index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import { Provider } from 'react-redux';
      import { createStore, applyMiddleware } from 'redux';
      import thunk from 'redux-thunk';
      import './index.css';
      import rootReducer from './reducers';
      import App from './App';
      import * as serviceWorker from './serviceWorker';
      
      // use applyMiddleware to add the thunk middleware to the store
      const store = createStore(rootReducer, applyMiddleware(thunk));
      
      ReactDOM.render(
        <Provider store={store}>
          <App />
        </Provider>,
        document.getElementById('root')
      );
      

      Sekarang, Redux Thunk diimpor dan diterapkan di aplikasi Anda.

      Menggunakan Thunk Redux di Aplikasi Sampel

      Kasus penggunaan paling umum untuk Redux Thunk adalah berkomunikasi secara asinkron dengan API eksternal untuk mengambil atau menyimpan data. Redux Thunk memudahkan pengiriman aksi yang mengikuti siklus hidup permintaan ke API eksternal.

      Membuat item agenda baru biasanya melibatkan pengiriman aksi terlebih dahulu untuk mengindikasikan bahwa pembuatan item agenda telah dimulai. Kemudian, jika item agenda berhasil dibuat dan dikembalikan oleh server eksternal, aksi lain akan dikirim bersama item agenda baru. Jika ada kesalahan dan agenda gagal disimpan di server, sebuah aksi bersama kesalahan dapat dikirim sebagai gantinya.

      Mari kita lihat cara melakukannya menggunakan Redux Thunk.

      Dalam komponen kontainer Anda, impor aksi dan kirimkan:

      src/containers/AddTodo.js

      import { connect } from 'react-redux';
      import { addTodo } from '../actions';
      import NewTodo from '../components/NewTodo';
      
      const mapDispatchToProps = dispatch => {
        return {
          onAddTodo: todo => {
            dispatch(addTodo(todo));
          }
        };
      };
      
      export default connect(
        null,
        mapDispatchToProps
      )(NewTodo);
      

      Aksi ini akan menggunakan Axios untuk mengirim permintaan POST ke titik akhir di JSONPlaceholder (https://jsonplaceholder.typicode.com/todos):

      src/actions/index.js

      import {
        ADD_TODO_SUCCESS,
        ADD_TODO_FAILURE,
        ADD_TODO_STARTED,
        DELETE_TODO
      } from './types';
      
      import axios from 'axios';
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(`https://jsonplaceholder.typicode.com/todos`, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              dispatch(addTodoSuccess(res.data));
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      const addTodoSuccess = todo => ({
        type: ADD_TODO_SUCCESS,
        payload: {
          ...todo
        }
      });
      
      const addTodoStarted = () => ({
        type: ADD_TODO_STARTED
      });
      
      const addTodoFailure = error => ({
        type: ADD_TODO_FAILURE,
        payload: {
          error
        }
      });
      

      Perhatikan cara pembuat aksi addTodo mengembalikan fungsi sebagai ganti objek aksi reguler. Fungsi itu menerima metode pengiriman dari penyimpanan.

      Di dalam isi fungsi, Anda mengirim aksi sinkron segera ke penyimpanan terlebih dahulu untuk mengindikasikan bahwa Anda telah mulai menyimpan agenda bersama API eksternal. Kemudian, Anda membuat permintaan POST sesungguhnya ke server menggunakan Axios. Pada respons yang berhasil dari server, Anda mengirim aksi sinkron yang berhasil bersama data yang diterima dari respons tersebut, tetapi bila respons gagal, kita mengirim tindakan sinkron yang berbeda bersama pesan kesalahan.

      Saat menggunakan API yang bersifat eksternal, seperti JSONPlaceholder dalam kasus ini, kita bisa melihat penundaan jaringan yang terjadi sesungguhnya. Namun, jika Anda menggunakan server backend lokal, respons jaringan mungkin menjadi terlalu cepat mengalami penundaan jaringan yang nanti dialami pengguna sesungguhnya, sehingga Anda dapat menambahkan penundaan buatan saat pengembangan:

      src/actions/index.js

      // ...
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(ENDPOINT, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              setTimeout(() => {
                dispatch(addTodoSuccess(res.data));
              }, 2500);
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      // ...
      

      Untuk menguji skenario kesalahan, Anda dapat melontarkan kesalahan secara manual:

      src/actions/index.js

      // ...
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(ENDPOINT, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              throw new Error('addToDo error!');
              // dispatch(addTodoSuccess(res.data));
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      // ...
      

      Untuk kelengkapan, inilah contoh bentuk reducer agenda untuk menangani siklus hidup penuh dari permintaan:

      src/reducers/todosReducer.js

      import {
        ADD_TODO_SUCCESS,
        ADD_TODO_FAILURE,
        ADD_TODO_STARTED,
        DELETE_TODO
      } from '../actions/types';
      
      const initialState = {
        loading: false,
        todos: [],
        error: null
      };
      
      export default function todosReducer(state = initialState, action) {
        switch (action.type) {
          case ADD_TODO_STARTED:
            return {
              ...state,
              loading: true
            };
          case ADD_TODO_SUCCESS:
            return {
              ...state,
              loading: false,
              error: null,
              todos: [...state.todos, action.payload]
            };
          case ADD_TODO_FAILURE:
            return {
              ...state,
              loading: false,
              error: action.payload.error
            };
          default:
            return state;
        }
      }
      

      Mendalami getState

      Selain menerima metode pengiriman dari status, fungsi yang dikembalikan oleh pembuat aksi asinkron bersama Redux Thunk juga menerima metode getState dari penyimpanan, sehingga nilai-nilai penyimpanan saat ini dapat dibaca:

      src/actions/index.js

      export const addTodo = ({ title, userId }) => {
        return (dispatch, getState) => {
          dispatch(addTodoStarted());
      
          console.log('current state:', getState());
      
          // ...
        };
      };
      

      Dengan hal tersebut di atas, status saat ini akan dicetak ke konsol saja.

      Misalnya:

      {loading: true, todos: Array(1), error: null}
      

      Penggunaan getState dapat digunakan untuk menangani hal-hal berbeda, bergantung pada status saat ini. Misalnya, jika ingin membatasi aplikasi pada empat item agenda saja untuk setiap kalinya, Anda dapat kembali dari fungsi jika status sudah berisi item agenda dalam jumlah maksimum:

      src/actions/index.js

      export const addTodo = ({ title, userId }) => {
        return (dispatch, getState) => {
          const { todos } = getState();
      
          if (todos.length > 4) return;
      
          dispatch(addTodoStarted());
      
          // ...
        };
      };
      

      Dengan hal tersebut di atas, aplikasi akan dibatasi pada empat item agenda.

      Kesimpulan

      Dalam tutorial ini, Anda telah mendalami penambahan Redux Thunk ke aplikasi React untuk memungkinkan pengiriman aksi secara asinkron. Hal ini berguna saat memanfaatkan penyimpanan Redux dan mengandalkan API eksternal.

      Jika Anda ingin mempelajari lebih lanjut tentang React, lihat seri Cara Melakukan Pengodean di React.js dari kami, atau baca halaman topik React kami untuk proyek pemrograman dan latihan.



      Source link