Our app is currently updating both the amount and currency code values using callback functions created in the ExchangeRate component and passed down to child components through props. With redux we can avoid this indirection.
useDispatch()
takes no arguments and simply returns a dispatch
function which can be used to fire actions into your reducers.
Actions dispatched are encouraged to follow the Flux-Standard-Action style: https://github.com/redux-utilities/flux-standard-action#flux-standard-action
Note:
The last 1 minute of this video is just me ranting about two things:
You can learn more about all of the redux hooks here: https://react-redux.js.org/api/hooks
Instructor: [0:00] Our app is currently updating both the amount and currencyCode with callback functions that we create here in exchangerate.js. With Redux, we can avoid this interaction. We can create those functions directly in the components that use them.
[0:14] For now, we'll comment out both of these. We can also get rid of the placeholder functions we created earlier. We'll also get rid of the props for onChange. Now, we can go into amountfield.js. At the top of the file, import { useDispatch } from "react-redux". At the top of our function declaration, type const dispatch = useDispatch().
[0:40] Dispatch is a function that allows us to send actions into our reducer. Those actions will potentially update the state. Let's create a new onChange function and take it out of our property list here. Function onChange will take as an argument an event.
[0:57] Then, inside of that function, we'll dispatch {type}. We'll call this action amountChanged, and for its payload, e.target.value. In this case, that'll be the value associated with our input field as we type in changes. Now, every single time we type into our field, we're going to dispatch the amountChanged event into our reducer.
[1:23] Let's go into store.js. Inside our reducer function, type if (action.type === amountChanged) return {...state, amount:action.payload}. With Redux and, frankly, React in general, we never directly modify the state. We're always working with this immutable one-way data.
[1:46] In this case, we don't modify the existing state object. We create a new state object using the existing state as a basis for our copy. Then, in our new object, we override the amount field with action.payload, which is the new amount coming from our dispatched amountChanged action.
[2:07] Any time that event is dispatched, we are updating the entire state in our reducer to a new state with a new amount. You can see the effect of that in our application here. As I type in new amounts, the selector recognizes them, and our exchangeRate component is updated immediately.
[2:25] We've gotten rid of handleAmountChange. Let's go ahead and take care of handleCurrencyCode. Open up currency code picker. We'll also import useDispatch, const dispatch = useDispatch().
[2:35] For our onChange function, instead of taking it in as a prop, we'll just create it here, function onChange, also taking e.
[2:47] We'll say dispatch type currencyCodeChanged. For the payload, just like before, e.target.value. Anytime we choose a new exchange rate over here in our application, we dispatch the currencyCodeChanged action.
[3:04] Going to store.js, let's change this to a switch statement. Type switch(action.type){case amountChanged, return that, case currencyCodeChanged. We'll return something very similar to that. Then default return existing state.
[3:27] In currencyCodeChanged, we don't want to update the amount filled. We want to update currencyCode. We've now got a switch statement. We switch over whatever the action type is. If it's amountChanged, we update the amount. If it's currencyCodeChanged, we update the currencyCode. You can see with that change in place, as I click through and change the currencyCode, it's getting updated as expected.
[3:49] One thing that's interesting about this, if you go back into exchangerate.js...We can remove some of this no longer used code here. If you go back in here, you'll notice that our useEffect still relies on currencyCode.
[4:04] Our useEffect hook here is relying on the result of a selector. You can see anytime I change the currencyCode, the exchange rates are updating, which indicates that there's no problem integrating Redux hooks with your typical built-in React hooks. They all work together exactly as you would expect they would.
[4:23] You also notice at this point, our application is starting to look really nice. We got rid of a whole bunch of callback creation in the upper-level component. This way, now the component only worries about the state that it needs to render its children. It doesn't have to handle the events of its children or do extra work other than populating the exchange rates.
[4:43] In my opinion, with just useSelector in our parent component and useDispatch in the children, we've significantly cleaned up our application.