Let me set the stage: you’ve come up with a great idea for a web app, and you’ve decided to build it with React. By way of completely arbitrary example, let’s say it’s a guitar chord finder with an interactive guitar neck and a submission form (like, say, ). this one Shameless self-promotion! You’ve made a couple to-do lists, watched a couple tutorials, and you think you’ve got the hang of all this “props” stuff, so you jump right in. It’s going well enough — you’ve got your top level app component, maybe a form, a guitar component… and then, it starts to dawn on you. You’re going to need a guitar… with six strings… with twelve frets each… which all need to communicate information about whether or not they’re muted, pressed, or open…which need to be able to communicate with the form and express a chord… Before you know it, six hours have passed, there’s yarn all over the walls, and you’re pretty sure you’ve uncovered a conspiracy inexplicably implicating completely fictional people and the United States Postal Service. Side note for Always Sunny fans, did you ever notice that “Pepe Sylvia” sounds an awful lot like how an illiterate person would try to read “Pennsylvania”? That’s the sort of subtle comedy we need. But fret not (pun intended)! There’s this cool tool called Redux you may have heard about, and it’s designed for scenarios just like this, when your component nesting is out of control and props are being passed around left and right. It’s arguably on the way out, thanks to React’s newly retooled Context API and other competing libraries, and it tends to stir up passionate feelings among developers who tend to alternatively think it’s either the best thing since sliced bread or an unholy obfuscating mess, but it definitely still has its place. The golden rule regarding Redux’s use, from its creators and devs the world over, is something approximating “If you’re not sure if you need Redux, you probably don’t.” To those people I say… well, you’re probably right. Redux add a lot of complexity and boilerplate for simple tasks, which we’ll soon find out. And, as a matter of fact, I build this guitar app with regular old React, and it worked out fine. But, I was young and naive then (two months ago), so for explanation’s sake, let’s find out how it could have made our lives easier. Once we get the boilerplate out of the way, Redux has the ability to make your apps more scalable, more performant, and easier to debug and reason about. Plus, I really like it! does did An artist’s rendering of a React app. Who’s got time for all that nesting? So let’s assume you’ve got a React app all built up. If not, check out one of the . Even try if you want. Once you’re up and running, the first thing we’ll want to do is install our packages! many great tutorials out there create-react-app sudo npm install reduxsudo npm install react-redux Astute readers may note that we’re installing both Redux and a React-specific version — what gives?! Well, Redux is actually a framework-agnostic library. You can use it with Backbone, or Mithril, or Angular, or Meteor, or Vue — it’ll work with all of them! The React specific version, though, has a few methods which will become very useful to us when we’re setting things up in our components. With installations out of the way, we’re going to set up our file structure, which has a few more pieces than what we’re used to in a traditional React app. Everyone does this a little bit differently, but typically I like to make an folder, a folder, and a folder inside my react-client/src directory, like so: actions reducers store You may see some folks use constants, I don’t bother with those. Great! So let’s begin with the — this is what makes Redux work. You can think of it as an external state container which React can directly interact with by means of a few special methods, regardless of their level of nesting in a project’s hierarchy. Need some state? Go to the ! Need to update the state? Dispatch an action to the ! Everything is connected! store components store store That’s right, I used the same reference twice in one article. It’s… a metaphor for how Redux makes you write the same thing in a few places in your app… or something. Let’s make an file inside of our folder. First, we’ll import React, so we have access to it — makes sense. Next, we’ll import two methods directly from Redux: and These will do just what they sound like! What are and why are we combining them, though? Let’s put that question on hold, except to say we’re also going to import our file, which we’ve yet to make in our folder. After all our import statements, we’re going to create a variable called and combine our inside of it with which takes an object as an argument and will become our global application state_._ Put it all together, and it looks like the below. index.js store createStore combineReducers. reducers reducers.js reducers store reducers combineReducers, Line 9 is exclusively for using Redux’s excellent browser extension. I highly recommend it, but it’s not mandatory. There is still one missing piece: Redux’s . This is a context wrapper which needs to surround your entire application and which receives the as a property, allowing all of your other to have access to it via Redux’s methods. At the very highest level in our app, our file where we’re attaching React to the DOM, we’ll simply import the , the , and our (which we’ll build in a second) and bundle everything up, like so. Provider component store components index.jsx Store Provider App component This way, our entire application has access to our store! Now that we have our set up, let’s talk about the rest of the Redux ecosystem. This is the most complicated part so bear with me. Effectively, you have a circular relationship that goes -> -> -> -> (seen below in diagram form). store Store Components Actions Reducers Store The ’s state is mapped onto a by way of a little function called — you can think of this as a subscription service. In fact, the native Redux method this utilizes is called (even though we won’t actually be using it directly in this tutorial). Any change made to the will flow down to any which has subscribed to that property in the , triggering a re-render. Neat, huh? store component mapStateToProps subscribe store component store In order to actually affect the state of the app, however, a has to use a separate function: . is another method, which takes an (which we import) and sends it off to a , which receives that and actually changes the state of the app. I know that’s a mouthful, but let’s take it in steps, starting with a super-simple . This will be our highest level , and it will also import a method known as from Redux as well as a method called from React-Redux. The former just makes writing our function a little cleaner, while the latter is a which bundles up with and so that these methods have access to the . To wit: component mapDispatchToProps Dispatch store action reducer action component App component bindActionCreators connect mapDispatchToProps higher-order component our component mapStateToProps mapDispatchToProps store Notice how we’re importing our action on line 5_._ There are a couple other small points of order here — notice how takes in the application state as a parameter, and how we pluck out individual properties from that state. These properties will be accessible on that ’s , so if we wanted to get , we would do it like so in our ’s render function (note the <p> tag): mapStateToProps component props examplePropOne component render() {return (<div><h1>Hello world, this is a Redux tutorial!</h1><p>Here is our property: {this.props.examplePropOne}</p></div>)} This way, any changes made to even if from another , will flow through and be rendered in this . You don’t have to pick out all the properties from the state object in your function — only bring in the ones your cares about! examplePropOne, component component mapStateToProps component , similarly, takes in as a parameter. is a special method of the Redux , and by associating our with it through , we are both attaching it to the ’s props and saying that, whenever it’s invoked, it will dispatch the in question to the to be ultimately turned into state in the . mapDispatchToProps dispatch Dispatch store action bindActionCreators component action reducer store Here’s an example of how might look in a function on the same component: that exampleFunction() {this.props.exampleAction();} Still with me? Last thing to note in the is the syntax of the method — it will always take two arguments, and they will always be and , in that order. You can omit easily, but if you want to omit on a specific , be sure to leave a value in its place. The name of the itself should be invoked at the very end of the statement. Here’s another example, this time written without and with a called : component connect mapStateToProps mapDispatchToProps mapDispatchToProps mapStateToProps component null component connect mapStateToProps component List export default connect(null, mapDispatchToProps)(List); So far we’ve taken care of the and parts of the Redux life-cycle. That just leaves and . store component actions reducers are the easy part — they’re just functions that create JavaScript objects, with one compulsory property called . By convention, is typically written in all caps and is very descriptive of what that actually does. It often has another property called , in which we put any actual data being passed into the . In general, we want to avoid including any actual application logic inside of these : they should simply deliver a and potentially some data. Here’s how our file might look: Actions type type action payload action actions type actions/actions.js Notice how the second action receives an argument, which it passes along as a payload. That’s really all there is to ! actions At this point, we only have one item left in our life-cycle: . A is effectively a giant switch statement that takes in an and, depending on its , updates the application’s state. If you remember, when we created our above, we imported our and passed them through in our function, meaning that whatever returns out of our will update the state of the . Those changes will then flow down through any functions that we put in our to subscribe to the , and our data will be fully linked. That’s Redux! We did it! reducers reducer action type store reducers combineReducers createStore reducers store mapStateToProps components store Now, there is one important concept I’ve left out so far — indeed, it’s one of the most important, foundational concepts in Redux: . In a nutshell, with Redux, for the purposes of easier debugging, clean, singleton code, and a more performant application, we never actually directly mutate the application’s state. Instead, every single state change is recorded as a snapshot in time, and every change is made to a copied version of the state. For a most obvious example of how this is useful, take the Redux dev tools alluded to earlier: it keeps a log of so that you can see step-by-step how and when things are changing. Super useful! I highly recommend poking around with it if you have the time. immutability the entire of history of state in the entire app’s life-cycle All that being said, here’s an example of what a might look like — that is, a function which takes in the application’s state, as well as incoming , runs it through a switch statement, and returns the updated version of the state to deliver to the : reducer actions store We do not have to provide the default state as I did in this example—in some apps, you will define a default state in a different file, give it initializing properties, and then import it into your . In other apps, you might simply initialize the state as an empty object using ES6’s default parameter. There are no hard and fast rules to initializing state except that it must be defined, and your must return a state. For that purpose, it’s also very important that we don’t forget our default case on line 18. reducer reducer always Crucially, the should take in a state object and an as arguments, set up a switch on the ’s property, and then return a copied version of the state with the desired updated properties. Note the spread operators on lines 10 and 15: these spread the previous state before adding and/or modifying the desired properties, meaning you’ll end up returning a copied, slightly modified version of the state to the with every . You could also do this using Object.assign, but I find the spread operator more elegant. Note also how on line 16 we’re using the property to deliver data into our state, whereas on line 11 we’re hard-coding a modification to . reducer action action type store action action’s payload examplePropOne There’s a fair bit more we could talk about when it comes to React-Redux. Take, for example, asynchronous actions: passing around the results of API calls, operations which most of the time are pretty painless, can potentially cause a headache if you have that require certain criteria to be met before executing. There’s a library called to help deal with that. You can also have multiple in one project, which you would then pass in through the function in your file. Additionally, local state can still be really useful in individual , and you should really only involve the in cases where you have state being passed around between nested I encourage you to read more and start experimenting to learn all the finer points of Redux, and to read about competing state management solutions including mobx and the aforementioned Context API. actions redux-thunk reducers combineReducers store components store components. At this point, though, I hope you can see how Redux might make our guitar app a bit simpler. Say a user clicks on a fret, and we need to update the string it’s on, the guitar , the app , and the form . Instead of passing around all over the place, through multiple levels where it may not even be used, we can instead dispatch a single and set up a couple subscriptions and we’re good to go! In especially large apps with a lot of nesting and a lot of state to manage, this can make your life a lot simpler, and is one possible solution to a very sticky problem. component component component props action If you made it this far, thanks for reading! Here is a cute ferret as a thank you.
Share Your Thoughts