State, a term from React, is an object that holds data of a component. State is useful for making dynamic pages through conditional rendering. When developers work on complex applications, they need external tools to manage the application’s state. Developers often use state management libraries like Redux, Context API, Unstated, Flux, and Mobx to create a model of their application state, to update the state of components and to observe changes to the state, and to read state values.
Let’s get into state management using redux …
How?
Each component has stated. If one component needs to access another component’s state? Or change data in one state and still see the changes on another component? Redux came up with an answer to solve this, which is Store which has state or states that operate on a global level. This state is accessible by all the components and changes made to one state reflect throughout the application.
According to its official documentation, Redux was described in three fundamental principles:
- The state of the entire application is stored in an object tree within a single store.
- Ensure the state is read-only and emit/dispatch an action to change the state.
- Write pure reducer functions to transform the state using action.
Main Concepts of Redux
Redux introduces actions, action creators, reducers, and stores. These concepts are used to create a simple state management architecture.
Actions are in the form of normal Javascript objects, containing a type and an optional payload.
Action Creators are functions that return action objects. And then, the returned object is sent to various reducers in the application.
A reducer is a pure function that takes the previous state and dispatched action as parameters and returns the new application state. For complex and huge applications, reducers will be split off into smaller reducers to manage certain parts of the state tree.
The Redux store is the application state stored as objects. Whenever the store is updated, it will update all the components subscribed to it. The store is responsible for storing, reading, and updating the state.
Coming to the flow
Store: Redux.createStore is a method which creates a Redux store that holds the entire state tree of your application. There should only be a single store in your application.
Syntax
const store = createStore([reducer],[preloadedState],[enhancer]);
Here, reducer is a method that takes the previous state and action and returns a new state. preloadedState and enhancer are optional parameters.
preloadedState would be an initial state for the reducer.
enhancer is used to enhance the store with third-party capabilities such as middleware, time travel, persistence, etc.
const store = Redux.createStore(messageReducer); |
Reducer
In the below snippet, messageReducer is a reducer with 2 parameters which are state (previous state, action). There may be any number of switch cases, based on the dispatched action the state will be changed. Here, ADD is adding a new message to the previous state.
const messageReducer = (state = [], action) => { switch (action.type) { case ‘ADD’: return [ …state, action.message ]; default: return state; } }; |
Actions
addMessage is a method that returns an action which is a normal JS object. Action should contain type (one of the cases in reducer switch statement), optional payload, here message.
const addMessage = (message) => { return { type: ADD, message: message } }; |
View
In this example, there will be an input field and submit. On clicking the submit button, the user entered text will be added in a list below. On submitting, an action will be dispatched which is addMessage.
// React: class Presentational extends React.Component { constructor(props) { super(props); this.state = { input: ” } this.handleChange = this.handleChange.bind(this); this.submitMessage = this.submitMessage.bind(this); } handleChange(event) { this.setState({ input: event.target.value }); } submitMessage() { this.props.submitNewMessage(this.state.input); this.setState({ input: ” }); } render() { return ( <div> <h2>Type in a new Message:</h2> <input value={this.state.input} onChange={this.handleChange}/><br/> <button onClick={this.submitMessage}>Submit</button> <ul> {this.props.messages.map( (message, idx) => { return ( <li key={idx}>{message}</li> ) }) } </ul> </div> ); } }; // React-Redux const Provider = ReactRedux.Provider; const connect = ReactRedux.connect; const mapStateToProps = (state) => { return {messages: state} }; const mapDispatchToProps = (dispatch) => { return { submitNewMessage: (message) => { dispatch(addMessage(message)) } } }; const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational); class AppWrapper extends React.Component { render() { return ( <Provider store={store}> <Container/> </Provider> ); } }; ReactDOM.render(<AppWrapper />, document.getElementById(‘app’)); |
The connect() connects a React component to the Redux store.
The Redux Store provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store.
The <Provider /> makes the Redux store available to any nested components that have been wrapped in the connect() function.
Most of the applications will render an <Provider> at the top level since any React component in a React-Redux app can be connected.
The UI will look like,
mapStateToProps is a method that would provide the store data to your component. The parameter to mapStateToProps is store data, here the state returned by messageReducer
And mapDispatchToProps is something that would provide the action creators as props to your component
Conclusion
Redux is one of the useful libraries to manage your React application state. There are other options to do state management in your application like Context API. For more information about Redux, you can refer to its official documentation and its middlewares like Redux-Thunk and Redux-Saga.
More such informative blogs are here.