React Redux
Overview
React bindings for Redux
Redux can be used with any UI layer (such as Angular, Vue, or plain JS), but is most commonly used with React.
React-Redux
provides bindings between React and Redux.
connect
is used in Class Components
The connect
function generates wrapper "container" components that subscribe to the store, so you don't have to write store subscription code for every component that needs to talk to the store.
Any component in your application can be wrapped with connect
and "connected" to the store. Connecting more components is usually better for performance.
useSelector
and useDispatch
Hooks are used in Function Components
These hooks provide access to the data in the Store
and the ability to dispatch actions to the Store
to change the data.
Provider
gets you access to the Store
Provider
gets you access to the store from anywhere in the component hierarchy.
Putting a <Provider>
component around your root component makes the store accessible to all connected components.
useSelector
and useDispatch
Hooks provided by the React Redux library : useSelector
Allows you to select
(think SQL) slices of state from the Redux Store
.
useState
manages Local Component StateuseSelector
manages Redux Store State
The useSelector
hook gets state (data) directly from the Redux Store
so you there is no need to pass the state from the Redux store in as a prop
to the component.
Serves a similar purpose to mapStateToProps
when using class-based components and connect
.
useDispatch
Gives you a reference to the dispatch function so you can dispatch actions and change the data (state
) in the Store
.
Example
connect
function
The What it Does
- Automatically handles subscribing to the store, and helps dispatch actions
- Performance optimizations - automatically implements shouldComponentUpdate, and only re-renders your component when the data it needs changes
- Separates "subscribing to the store" from "what store am I subscribing to, and where did it come from?"
- Helps keep your React components "unaware" of Redux
Using
The connect
function takes two arguments, both optional:
- mapStateToProps: called every time the store state changes. It receives the entire store state, and should return an object of data this component needs.
mapStateToProps
could have been namedmapStoreStateToProps
ormapReduxStateToProps
because people confuse React's local component state and Redux's state held in the store. See this issue for more information.
- mapDispatchToProps: called once on component creation. It receives the dispatch method, and should return an object full of functions that use dispatch. For both functions, each field in the returned object becomes a prop for the wrapped component.
connect
returns a new function that accepts the component to wrap, and that function returns the wrapper component.
Example
mapState
Functions
Writing connect
will re-run your mapState function every time the store state changes- does shallow equality comparison between the last and current result objects
- re-renders your component if any fields are === different than the last result
- if function passes two parameters:
- receives wrapper component's props as the second argument
- called whenever the props passed to the wrapper component change (in addition to when store state changes)
- a mapState function should be pure
- no side effects
- just state ( + wrapper props) in
- new component props out
- similar to reducer
mapDispatch
Functions
Writing - By default, connect gives your components
props.dispatch
. - If you want to "bind" action creators instead, you can provide a mapDispatch function as the second argument to connect. It gets dispatch as an argument, and you can return functions that will dispatch automatically when called.
- Declaring mapDispatch with two arguments works the same way as mapState - will be given ownProps, and re-run when they change
- Better choice: pass an object full of action creators as the second argument to connect (the "object shorthand")
Tips
- You can connect as many of your app's components as you want, not just the root component.
- connecting more components is usually better for performance
- Connect components when
- they need to access store state or dispatch actions
- where passing props down multiple levels would be a pain
- Only declare your map* functions with two arguments if you really need props
- Both map* functions are optional
- if you only need data, use connect(mapState)(MyComponent).
- if you only need to dispatch, use connect(null, mapDispatch)(MyComponent)
- In general, don't write an actual
mapDispatch
function yourself- use the "object shorthand" form with action creators instead!
Provider
Wrapping your root application component in <Provider>
and passing it the store reference makes that store available to all connected components in the component tree.
Why is the Provider designed this way?
- Manually importing the
store
ties your components to that specific store instance, making it harder to test them - React-Redux's
<Provider>
acts as a lightweight dependency injection approach, which lets you reuse Redux-connected components and test them with a fake store if needed
Inside React Redux (optional)
connect
Inside How does connect work internally or how would you manually create a container component (connected, wrapped)?
Using Redux with any UI layer requires the same consistent set of steps:
Create a Redux store
Subscribe to updates
Inside the subscription callback:
- Get the current store state
- Extract the data needed by this piece of UI
- Update the UI with the data
If necessary, render the UI with initial state
Respond to UI inputs by dispatching Redux actions
connect
Works
Demo of How In this demo we are going to:
Use the redux demo code from the previous concept chapter on Redux shown below:
- commenting out the last lines that manually dispatch actions (because there is no UI yet)
//action typesconst INCREMENT = "INCREMENT";const DECREMENT = "DECREMENT";//action creatorsfunction increment() {return { type: INCREMENT };}function decrement() {return { type: DECREMENT };}//reducerfunction reducer(state = 0, action) {switch (action.type) {case INCREMENT:return state + 1;case DECREMENT:return state - 1;default:return state;}}//storevar store = Redux.createStore(reducer);// store.subscribe(logState);// store.dispatch({ type: '' });// store.dispatch(increment());// store.dispatch(increment());// store.dispatch(decrement());// store.dispatch(decrement());// store.dispatch(decrement());// store.dispatch(decrement());Add a UI by creating components including manually creating the container component.
Add this code right below the last line in the previous step.
Open the application in a browser: http://localhost:5000/
Click the
+
and-
buttons to verify it is workingComment out the manually created component
// class WrappedCounterManual extends React.Component {// state = this.getCurrentStateFromStore();// getCurrentStateFromStore() {// return {// count: store.getState()// };// }// updateStateFromStore = () => {// const currentState = this.getCurrentStateFromStore();// if (this.state !== currentState) {// this.setState(currentState);// }// };// componentDidMount() {// this.unsubscribeStore = store.subscribe(this.updateStateFromStore);// }// componentWillUnmount() {// this.unsubscribeStore();// }// render() {// return (// <Counter// count={this.state.count}// increment={() => store.dispatch(increment())}// decrement={() => store.dispatch(decrement())}// />// );// }// }// const element = (// <div>// <h3>Container Manually Implemented </h3>// <WrappedCounterManual />// </div>// );// const rootElement = document.getElementById('root');//ReactDOM.createRoot(rootElement).render(element);Use
connect
to automatically create the container component.Add this code to the bottom of the file.
const mapStateToProps = (state) => {return {count: state,};};const mapDispatchToProps = { increment, decrement };const WrappedCounter = ReactRedux.connect(mapStateToProps,mapDispatchToProps)(Counter);const element = (<div><h3>Container Created using React Redux (connect)</h3><ReactRedux.Provider store={store}><WrappedCounter /></ReactRedux.Provider></div>);const rootElement = document.getElementById("root");ReactDOM.createRoot(rootElement).render(element);
Final Code
Provider
Inside How does the wrapped component get access to the store?
- The
Provider
puts it in the React context.