Something I recently started learning about while buffing up my React skills was the Redux library. I was asked a question at an interview about state management in React and while I had a basic understanding of state/props and hooks I decided to look further into the topic following the question. A common answer and natural next step in state management seemed to come in the form of Redux.
What is Redux?
Redux is a JavaScript library used for application state management storing all of the applications state in a single object tree (store).
Why all in one place you ask? To maintain a single source of truth when it came to the global state of the application. Instead of passing your state around and handling specific component trees states all separately it was all centralized.
State is read only when it comes to redux however. You don't transform or mutate state in the sense you generally would accessing and updating the data inside. You can read the data that is stored in there and transfer it to components in the form of props. When you "update" it however you're not actually updating the current single object tree but rather replacing it entirely with a new object.
How is that done? Changes are made with pure functions. These functions are called reducers. A reducer takes in a previous state and an action and produces the new state that will take its place. A small application can work with a single reducer or if the application gets larger you can then write separate reducers for handling specific parts of the store.
Why is that useful?
Well if you've had some level of exposure to developing in React you know that you can only pass data from state down to child components in the form of props. In other words it has one way data flow. The only way to communicate back up to the location of the state is by also passing down a callback function as props to then communicate at the top level to change the state. Now imagine you need some data 3, 4 or maybe 5 component levels down the component tree. You would have to drill that prop multiple levels deep to get that data where it needs to be and also the callback function at that. Not every component now housing those props even needs them though, it creates a lot of clutter and hinders the ability to make reusable components.
Redux solves that problem.
Instead of needlessly drilling the state down as props we can instead grab it from the single source of truth also known in Redux as the store. You're able to grab this from the store and set it as props within whatever component so needed through the connect function built in to Redux.
A Quick Demo on Connect
Let's first set up Redux in a React project.
So the first step would be to install redux and since we're doing it for react specifically react-redux. I will be using npm personally.
npm install redux react-redux
Now that its installed lets import a couple things into our index.js file.
import { createStore } from 'redux';
import { Provider } from 'react-redux';
I'm importing createStore from redux because we will be creating that centralized place of data at the root of the app. I also imported Provider because that's what we will be wrapping our application in so it has access to the store.
So first things first lets create that store in the same file.
const store = createStore();
Now lets wrap our application in the Provider and pass in the store as an attribute
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
We're not done yet. The store needs that reducer I mentioned earlier. Think of the reducer as the control behind the store. In my src folder I created a reducer.js file. In here I'm going to create a basic reducer. Reducers take in state and an action. Action is how you would create a new state but we're not going to be doing that today I'd just like to show you how to grab data from the store.
const initState = {
Jedi: [
{ id: 1, name: 'Luke Skywalker' },
{ id: 2, name: 'Obi-Wan Kenobi' },
],
Sith: [
{ id: 1, name: 'Emperor Palpatine' },
{ id: 2, name: 'Darth Vader' },
]
}
const reducer = (state = initState, action) => {
return state;
}
export default reducer;
So I passed in my initState object as the initial state of my reducer and exported the reducer out. All I'm doing with the reducer at this moment is returning our state with it. Now back to the index.js lets import that reducer we just made. Following that we pass the reducer into the store we created.
import reducer from './reducer';
const store = createStore(reducer);
Alright so we're all set up. One more time just to review. We installed redux and react-redux. We then imported createStore and Provider to our index.js where our app is being rendered from. We created a store and a reducer and passed the reducer into the store. We then wrapped our application in the Provider and provided it with the store we created so now it can provide our entire application with that store of data. Whew.
Now to check out how we can grab that data from anywhere in the application.
I'm simply going to create a component that houses a button. When you click the button it will log to the console the data from the store.
Here's what my component looks like:
import React, { Component } from 'react';
class LogButton extends Component {
render(){
const handleClick = () => {
console.log(this.props);
}
return(
<div>
<button onClick={handleClick}>Click Me</button>
</div>
);
}
}
Nothing fancy. What we have to do to grab data from the store our application has access to is use the connect function included with react-redux. So go ahead and import that in.
import { connect } from 'react-redux';
Connect is a higher order component that we will be wrapping our component in. So where you export your component instead pass it into the connect function like so:
export default connect()(LogButton);
Okay so now that our component is wrapped with the connect provided we now have access to the state of the store. All we have left to do is to map our state of the store to the props of the component. You assign what values you need from the store and map it to props and then pass that function into our connect like so:
const mapStateToProps = (state) => {
return {
jedi: state.Jedi,
sith: state.Sith
}
}
export default connect(mapStateToProps)(LogButton);
Now make sure to import the button into the root App component and lo and behold when you click the button it logs the data from the store that was mapped to the component.
That seems like an awful lot of work for such a small amount of data and it is. But now I can use connect in any component moving forward to grab data from the store without passing it around as props or handling state in my components.
Wrap up
Today was a demonstration on what redux is and what it means when it comes to data handling. I broke it down to its very base level and its truly not necessary for such a small scale app. When your project gets larger and larger and you find yourself passing state/props around and drilling between components over and over again, redux may be the solution in consolidating your data.
We went over how to bring redux into your application as well as how to grab data from the store but next week I'd like to talk about how to edit the store through the use of actions in the reducer.
I hope this was helpful to somebody wondering what redux is and it shed some light on the subject, as always... thanks for checking this out and happy coding!
Top comments (0)