Redux
- Install redux, react-redux, and update the react-dom
- Create a reducer for storing the state, and handle the dispatched task
- Create the store in index.js, and provide it to the app by <Provider…></Provider>
- Using redux in your component:
- Class-based component: In your component, create mapStateToProps, and mapDispatchToProps. Then use import {connect} from 'react-redux'; to connect your component to the store.
- Functional-based component: Use {useSelector, useDispatch} from 'react-redux'.
useSelector
obtains the values, anduseDispatch
dispatches the actions.
Install
npm install react react-redux npm update react react-dom
Create Reducer
create a file call reducer.js
, put in under /src/store/. We also put the initial state there.
const initialState = { counter: 0 } const reducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT': return { ...state, counter: state.counter + 1 } case 'DECREMENT': return { ...state, counter: state.counter - 1 } case 'INCREMENT5': return { ...state, counter: state.counter + action.value } case 'DECREMENT5': return { ...state, counter: state.counter - action.value } default: return state; } } export default reducer;
Create Store in index.js, and Provider it to the app
Here we create the store by using createStore, passing out reducer to it. Finally provide it to the app by <Provider store={…}…></Provider>
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; import {createStore} from "redux"; import reducer from "./store/reducer"; import {Provider} from 'react-redux'; const store = createStore(reducer); ReactDOM.render( <Provider store={store}> <App/> </Provider>, document.getElementById('root')); registerServiceWorker();
Connect Out Component to it
Old Way: For Class Base Component
The key here is to create a mapStateToProps
to get the state from redux, and a mapDispatchToProps
to dispatch actions to the redux. We connect redux to our component by using export default connect(mapStateToProps, mapDispatchToProps)(Counter); at the end.
Since the state and action are now the props, we can use them and call them in our component like this.props…
.
The 'action' we dispatch usual has a type
for the type of action, and any addition fields if needed.
import React, {Component} from 'react'; import CounterControl from '../../components/CounterControl/CounterControl'; import CounterOutput from '../../components/CounterOutput/CounterOutput'; import {connect} from 'react-redux'; class Counter extends Component { render() { return ( <div> <CounterOutput value={this.props.ctr}/> <CounterControl label="Increment" clicked={this.props.onIncrementCount}/> <CounterControl label="Decrement" clicked={() => this.props.onDecrementCount()}/> <CounterControl label="Add 5" clicked={() => this.props.onInc5Count()}/> <CounterControl label="Subtract 5" clicked={() => this.props.onDec5Count()}/> </div> ); } } const mapStateToProps = state => { return { ctr: state.counter } }; const mapDispatchToProps = dispatch => { return { onIncrementCount: () => dispatch({type: 'INCREMENT'}), onDecrementCount: () => dispatch({type: 'DECREMENT'}), onInc5Count: () => dispatch({type: 'INCREMENT5', value:5}), onDec5Count: () => dispatch({type: 'DECREMENT5', value:10}), }; } export default connect(mapStateToProps, mapDispatchToProps)(Counter);
The action.type needs to match the one in the reducer! Any spelling mistake/mismatch would cause the reducer to do nothing. You might want to use a separated file, and include this file on both the reducer and the component.
New Way: Use Hooks For Functional Base Component
People in these days use functional base component for react, and here is how to use redux in it by using useSelector
, useDispatch
and hooks.
import './App.css'; import {useSelector, useDispatch} from 'react-redux' function App() { const count = useSelector(state => { return state.counter; } ) const dispatch = useDispatch(); const click = (action, value) => { if (value) { dispatch({type: action, value: value}); } else { dispatch({type: action}); } } return ( <> <div>The counter is: {count}</div> <button onClick={() => click('INCREMENT')}>INCREMENT</button> <button onClick={() => click('DECREMENT')}>DECREMENT</button> <button onClick={() => click('INCREMENT5', 5)}>INCREMENT 5</button> <button onClick={() => click('DECREMENT5', 5)}>DECREMENT 5</button> </> ) } export default App;