We will learn how to dispatch a Redux action after the data has been fetched and recap how to do it when the route changes.
[00:01] I can extract the common code between the lifecycle hooks into a separate method. I will call it "fetchData." It will fetch the data for the current props.
[00:12] In this case, the data we want to fetch only depends on the filter. I'm calling this method from the componentDidMount hook, to fetch the initial data, and I also call it whenever the filter changes inside the component it update, lifecycle hook.
[00:29] We want the fetched routers to become a part of the Redux Store state. The only way to get something into the state is to dispatch an action. I'm going to call the callback prop called "receive todos," whether to todos I just fetched.
[00:48] To make it available inside the component, I need to pass a function called receive todos hat would be an action creator inside the second argument to connect. Since the name of the function will match the name of the callback prop, I can use the shorter ES6 prop edition.
[01:08] I will import receive todos from the file where I define all other action creators. Now, I can open that file with the action creators and actually implement it.
[01:19] I'm creating a new export of function called receive todos that takes the server response as an argument and returns an object with the type of receive todos and the response as a field.
[01:34] The reduce is handling this action. We'll need to know which filter the response corresponds to so I'm adding a filter as an argument to the receive todos action creator and I'm passing it as part of the action object.
[01:49] I'm going back to my component so I can pass the filter through the action creator. I'm destruction the filter and receive todos from props using ES6 destruction syntax. It's important that I destruct the filter right away, because by the time the callback fires this props filter might have changed because they use it and might have navigated away.
[02:12] If around the app now, I will see Redux actions being dispatched, ready to be handled by the reducers. They include all the necessary information, such as the filter and the server response.
[02:25] As I navigate through the app, the component fetches data in lifecycle hooks and dispatches Redux actions when the data is ready. As a final touch, I like to write less plate code when importing action creators.
[02:40] I'm replacing name to imports with the namespace "import" so that any function exported from the actions file will be in the object called "actions," which I will pass as a second argument to connect.
[02:57] I'm restructuring the props, because the toggle to the action creator needs to be passed under on todo click callback prompt name, but the rest of the props can be passed as the other.
[03:09] The rest object now continues all the props other than to toggle todo, so I will pass them through. I will also pass toggle todo as on todo click prop, because that's what todo list component expect.
[03:24] Finally, I'm splitting it over multiple lines for a little better readability. Let's recap how we fetch asynchronously in response to route changes. In the file where I define action creators, I add a new action creator called receive todos that accepts the filter and the response as arguments.
[03:47] It produces a regular Redux action object that can be dispatched after the request of the server has completed. I created a new react class component so that I can use lifecycles hooks, such as componentDidMount and componentDidUpdate to fetch the data.
[04:05] I'm fetching the data when the component mounts for the first time and if the filter prop, provided by react router changes, which means that user has navigated in the app. The visible todo list is the container component that enhances a presentational component called todo list when the data fetch an object.
[04:26] However, the class who wrote does not subscribe to the store itself so we wrap it in a connect call so that the containers generated by react Redux connect subscribes to the store and passes the props through the visible todo list component we wrote.
[04:43] Finally, the component generated by the router subscribes to the router changes so we have access to the router params in map state to props. The components we want to export are the result of colon disrupters so I reassign visible todo list and export it.
[05:02] Inside the map state of props, I read the filter value from the parameters supply direct order or use the default value. I used to only use filter for calculating the visible todos, but now, I also want to pass it as a prop so that it's available inside the component.
[05:21] Having access to the filter prop, let's determine whether I should fetch the data and pass it to the function that fetches todos. I import this function from the file where I define our fake API, which can be replaced by real API client.
[05:39] We fetch data when the component mounts and when the filter prop changes. Fetch lose return the promise so when it results, I use the response, to dispatch an action receive todos with the filter and the server response.
[05:55] The receive todos function available on the props dispatches the action returned by receive todos action creator automatically. This happens because I pass at this part of the actions object to connect.
[06:08] The actions object contains all the exported action creators defined in the actions.js file, because I use the ES6 namespace import syntax. The receive todos action creator does not automatically dispatch an action, but the receive todos prop injected by connect does.
[06:29] When I call it, it will call my action creator with the same arguments and dispatch the resultant action to the Redux Store. Inside the rented method, I rented to the presentational todo list component.
[06:43] Now that all action creators are injected as props with the same names, I have to grab toward todo specifically and pass it as onto click called by prop that todo list expects, but the rest props can be passed with their existing names.
Firstly, I thank you for your course. It's very useful for completely understanding Redux. But I have one small question here. Did you forget about updating code for the reducer todos.js with the new action “RECEIVE_TODOS” ? Finally, the Redux store couldn’t save the fetched data todos from the faked API. Thank you in advance for your response.
There is one small annoying thing in the code. When you set proper types
VisibleTodoList.propTypes = { filter: PropTypes.oneOf(['all', 'active', 'completed']).isRequired, receiveTodos: PropTypes.func.isRequired, toggleTodo: PropTypes.func.isRequired, };
Then you have one time warning (see bellow) Is there any chance to postpone creation of component to not have such warninings?
warning.js:36 Warning: Failed prop type: The prop
receiveTodos
is marked as required inwithRouter(Connect(VisibleTodoList))
, but its value isundefined
. in withRouter(Connect(VisibleTodoList)) (created by App) in App (created by Route) in Route (created by Root) in Router (created by BrowserRouter) in BrowserRouter (created by Root) in Provider (created by Root) in Root printWarning @ warning.js:36 warning @ warning.js:60 checkReactTypeSpec @ checkReactTypeSpec.js:80 validatePropTypes @ ReactElementValidator.js:162 createElement @ ReactElementValidator.js:216 App @ App.js:32 (anonymous) @ ReactCompositeComponent.js:305 measureLifeCyclePerf @ ReactCompositeComponent.js:75 _constructComponentWithoutOwner @ ReactCompositeComponent.js:304 _constructComponent @ ReactCompositeComponent.js:279 mountComponent @ ReactCompositeComponent.js:187 mountComponent @ ReactReconciler.js:45 performInitialMount @ ReactCompositeComponent.js:370 mountComponent @ ReactCompositeComponent.js:257 mountComponent @ ReactReconciler.js:45 performInitialMount @ ReactCompositeComponent.js:370 mountComponent @ ReactCompositeComponent.js:257 mountComponent @ ReactReconciler.js:45 performInitialMount @ ReactCompositeComponent.js:370 mountComponent @ ReactCompositeComponent.js:257 mountComponent @ ReactReconciler.js:45 performInitialMount @ ReactCompositeComponent.js:370 mountComponent @ ReactCompositeComponent.js:257 mountComponent @ ReactReconciler.js:45 performInitialMount @ ReactCompositeComponent.js:370 mountComponent @ ReactCompositeComponent.js:257 mountComponent @ ReactReconciler.js:45 performInitialMount @ ReactCompositeComponent.js:370 mountComponent @ ReactCompositeComponent.js:257 mountComponent @ ReactReconciler.js:45 mountComponentIntoNode @ ReactMount.js:104 perform @ Transaction.js:143 batchedMountComponentIntoNode @ ReactMount.js:126 perform @ Transaction.js:143 batchedUpdates @ ReactDefaultBatchingStrategy.js:62 batchedUpdates @ ReactUpdates.js:97 _renderNewRootComponent @ ReactMount.js:319 _renderSubtreeIntoContainer @ ReactMount.js:401 render @ ReactMount.js:422 (anonymous) @ index.js:22 (anonymous) @ bundle.js:47 webpack_require @ bundle.js:20 (anonymous) @ bundle.js:40 (anonymous) @ bundle.js:43 warning.js:36 Warning: Failed prop type: The proptoggleTodo
is marked as required inwithRouter(Connect(VisibleTodoList))
, but its value isundefined
. in withRouter(Connect(VisibleTodoList)) (created by App) in App (created by Route) in Route (created by Root) in Router (created by BrowserRouter) in BrowserRouter (created by Root) in Provider (created by Root) in Root