Basic Tutorial
To see how to use React-Redux in practice, weâll show a step-by-step example by creating a todo list app.
A Todo List Example
Jump to
- đ€ Just show me the code
- đ Providing the store
- âïž Connecting the Component
The React UI Components
We have implemented our React UI components as follows:
TodoApp
is the entry component for our app. It renders the header, theAddTodo
,TodoList
, andVisibilityFilters
components.AddTodo
is the component that allows a user to input a todo item and add to the list upon clicking its âAdd Todoâ button:- It uses a controlled input that sets state upon
onChange
. - When the user clicks on the âAdd Todoâ button, it dispatches the action (that we will provide using React-Redux) to add the todo to the store.
- It uses a controlled input that sets state upon
TodoList
is the component that renders the list of todos:- It renders the filtered list of todos when one of the
VisibilityFilters
is selected.
- It renders the filtered list of todos when one of the
Todo
is the component that renders a single todo item:- It renders the todo content, and shows that a todo is completed by crossing it out.
- It dispatches the action to toggle the todo's complete status upon
onClick
.
VisibilityFilters
renders a simple set of filters: all, completed, and incomplete. Clicking on each one of them filters the todos:- It accepts an
activeFilter
prop from the parent that indicates which filter is currently selected by the user. An active filter is rendered with an underscore. - It dispatches the
setFilter
action to update the selected filter.
- It accepts an
constants
holds the constants data for our app.- And finally
index
renders our app to the DOM.
The Redux Store
The Redux portion of the application has been set up using the patterns recommended in the Redux docs:
- Store
todos
: A normalized reducer of todos. It contains abyIds
map of all todos and aallIds
that contains the list of all ids.visibilityFilters
: A simple stringall
,completed
, orincomplete
.
- Action Creators
addTodo
creates the action to add todos. It takes a single string variablecontent
and returns anADD_TODO
action withpayload
containing a self-incrementedid
andcontent
toggleTodo
creates the action to toggle todos. It takes a single number variableid
and returns aTOGGLE_TODO
action withpayload
containingid
onlysetFilter
creates the action to set the appâs active filter. It takes a single string variablefilter
and returns aSET_FILTER
action withpayload
containing thefilter
itself
- Reducers
- The
todos
reducer- Appends the
id
to itsallIds
field and sets the todo within itsbyIds
field upon receiving theADD_TODO
action - Toggles the
completed
field for the todo upon receiving theTOGGLE_TODO
action
- Appends the
- The
visibilityFilters
reducer sets its slice of store to the new filter it receives from theSET_FILTER
action payload
- The
- Action Types
- We use a file
actionTypes.js
to hold the constants of action types to be reused
- We use a file
- Selectors
getTodoList
returns theallIds
list from thetodos
storegetTodoById
finds the todo in the store given byid
getTodos
is slightly more complex. It takes all theid
s fromallIds
, finds each todo inbyIds
, and returns the final array of todosgetTodosByVisibilityFilter
filters the todos according to the visibility filter
You may check out this CodeSandbox for the source code of the UI components and the unconnected Redux store described above.
We will now show how to connect this store to our app using React-Redux.
Providing the Store
First we need to make the store
available to our app. To do this, we wrap our app with the <Provider />
API provided by React-Redux.
Notice how our <TodoApp />
is now wrapped with the <Provider />
with store
passed in as a prop.
Connecting the Components
React-Redux provides a connect
function for you to read values from the Redux store (and re-read the values when the store updates).
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.mapDispatchToProps
: this parameter can either be a function, or an object.- If itâs a function, it will be called once on component creation. It will receive
dispatch
as an argument, and should return an object full of functions that usedispatch
to dispatch actions. - If itâs an object full of action creators, each action creator will be turned into a prop function that automatically dispatches its action when called. Note: We recommend using this âobject shorthandâ form.
- If itâs a function, it will be called once on component creation. It will receive
Normally, youâll call connect
in this way:
Letâs work on <AddTodo />
first. It needs to trigger changes to the store
to add new todos. Therefore, it needs to be able to dispatch
actions to the store. Hereâs how we do it.
Our addTodo
action creator looks like this:
By passing it to connect
, our component receives it as a prop, and it will automatically dispatch the action when itâs called.
Notice now that <AddTodo />
is wrapped with a parent component called <Connect(AddTodo) />
. Meanwhile, <AddTodo />
now gains one prop: the addTodo
action.
We also need to implement the handleAddTodo
function to let it dispatch the addTodo
action and reset the input
Now our <AddTodo />
is connected to the store. When we add a todo it would dispatch an action to change the store. We are not seeing it in the app because the other components are not connected yet. If you have the Redux DevTools Extension hooked up, you should see the action being dispatched:
You should also see that the store has changed accordingly:
The <TodoList />
component is responsible for rendering the list of todos. Therefore, it needs to read data from the store. We enable it by calling connect
with the mapStateToProps
parameter, a function describing which part of the data we need from the store.
Our <Todo />
component takes the todo item as props. We have this information from the byIds
field of the todos
. However, we also need the information from the allIds
field of the store indicating which todos and in what order they should be rendered. Our mapStateToProps
function may look like this:
Luckily we have a selector that does exactly this. We may simply import the selector and use it here.
We recommend encapsulating any complex lookups or computations of data in selector functions. In addition, you can further optimize the performance by using Reselect to write âmemoizedâ selectors that can skip unnecessary work. (See the Redux docs page on Computing Derived Data and the blog post Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance for more information on why and how to use selector functions.)
Now that our <TodoList />
is connected to the store. It should receive the list of todos, map over them, and pass each todo to the <Todo />
component. <Todo />
will in turn render them to the screen. Now try adding a todo. It should come up on our todo list!
We will connect more components. Before we do this, letâs pause and learn a bit more about connect
first.
connect
Common ways of calling Depending on what kind of components you are working with, there are different ways of calling connect
, with the most common ones summarized as below:
Do Not Subscribe to the Store | Subscribe to the Store | |
---|---|---|
Do Not Inject Action Creators | connect()(Component) | connect(mapStateToProps)(Component) |
Inject Action Creators | connect(null, mapDispatchToProps)(Component) | connect(mapStateToProps, mapDispatchToProps)(Component) |
Do not subscribe to the store and do not inject action creators
If you call connect
without providing any arguments, your component will:
- not re-render when the store changes
- receive
props.dispatch
that you may use to manually dispatch action
Subscribe to the store and do not inject action creators
If you call connect
with only mapStateToProps
, your component will:
- subscribe to the values that
mapStateToProps
extracts from the store, and re-render only when those values have changed - receive
props.dispatch
that you may use to manually dispatch action
Do not subscribe to the store and inject action creators
If you call connect
with only mapDispatchToProps
, your component will:
- not re-render when the store changes
- receive each of the action creators you inject with
mapDispatchToProps
as props and automatically dispatch the actions upon being called
Subscribe to the store and inject action creators
If you call connect
with both mapStateToProps
and mapDispatchToProps
, your component will:
- subscribe to the values that
mapStateToProps
extracts from the store, and re-render only when those values have changed - receive all of the action creators you inject with
mapDispatchToProps
as props and automatically dispatch the actions upon being called.
These four cases cover the most basic usages of connect
. To read more about connect
, continue reading our API section that explains it in more detail.
Now letâs connect the rest of our <TodoApp />
.
How should we implement the interaction of toggling todos? A keen reader might already have an answer. If you have your environment set up and have followed through up until this point, now is a good time to leave it aside and implement the feature by yourself. There would be no surprise that we connect our <Todo />
to dispatch toggleTodo
in a similar way:
Now our todoâs can be toggled complete. Weâre almost there!
Finally, letâs implement our VisibilityFilters
feature.
The <VisibilityFilters />
component needs to be able to read from the store which filter is currently active, and dispatch actions to the store. Therefore, we need to pass both a mapStateToProps
and mapDispatchToProps
. The mapStateToProps
here can be a simple accessor of the visibilityFilter
state. And the mapDispatchToProps
will contain the setFilter
action creator.
Meanwhile, we also need to update our <TodoList />
component to filter todos according to the active filter. Previously the mapStateToProps
we passed to the <TodoList />
connect
function call was simply the selector that selects the whole list of todos. Letâs write another selector to help filtering todos by their status.
And connecting to the store with the help of the selector:
Now we've finished a very simple example of a todo app with React-Redux. All our components are connected! Isn't that nice? đđ
Links
- Usage with React
- Using the React-Redux Bindings
- Higher Order Components in Depth
- Computing Derived Data
- Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance
Get More Help
- Reactiflux Redux channel
- StackOverflow
- GitHub Issues