Hi! Welcome to the last part of blog series about debouncing forms in React. Today I want to show you how can you add redux to your debounced component.
Adding redux
It may seem like overkill for this simple example but I decided to add redux to show how this debounced form can be used in the more realistic scenario.
So, after adding redux
and react-redux
to my application I started by creating actions & actions
creators under src/actions/index.js
:
exportconstADD_WORD='ADD_WORD'exportconstaddWord=word=>({
type:ADD_WORD,
word,})
To explain how those two can be used I added a small test in ‘actions.test.js`:
import{ADD_WORD, addWord }from'./index'describe('Actions',()=>{it('should create action to add word',()=>{const expectedAction ={
type:ADD_WORD,
word:'fake',}expect(addWord('fake')).toEqual(expectedAction)})})
As you can see calling addWord
with some string should dispatch action with ADD_WORD
type and typed
a word.
Next step was to add src/reducers/index.js
:
import{ combineReducers }from'redux'import{ADD_WORD}from'../actions/index'exportconstwords=(state =[], action)=>{switch(action.type){caseADD_WORD:return[action.word,...state]default:return state
}}const rootReducer =combineReducers({ words })exportdefault rootReducer
Where I have my pure function words
which is getting its own piece of state to work with. In this
case, I want my typed words to be first in a state of my application. I also added tests:
import{ words }from'./index'import{ADD_WORD}from'../actions/index'describe('Words reducer',()=>{it('should return initial state',()=>{expect(words(undefined,{})).toEqual([])})it('should handle ADD_WORD on initial state',()=>{expect(words([],{ type:ADD_WORD, word:'tom'})).toEqual(['tom'])})it('should handle ADD_WORD on existing state',()=>{expect(words(['tim'],{ type:ADD_WORD, word:'tom'})).toEqual(['tom','tim',])})})
The last thing is to set up my store and connect it with react application. The first step is happening in
store.js
:
import{ createStore, compose }from'redux'import rootReducer from'./reducers'const store =createStore(
rootReducer,compose(window.devToolsExtension ? window.devToolsExtension():f=> f))exportdefault store
I create here my store with rootReducer
which in this case is just only words
reducer and I also
added redux dev tools which help me debug my redux code.
The second step is to modify my index.js
so redux can be injected into my application:
import{ Provider }from'react-redux'import AppContainer from'./components/App/AppContainer'import store from'./store'const root =(<Provider store={store}><AppContainer /></Provider>)
ReactDOM.render(root, document.getElementById('root'))
Using redux with react applications
You may notice that Provider
component is wrapping a new one - AppContainer
. This is a nice pattern
to use when using redux & react applications. It boils down to two concepts: component and container.
A component is responsible only for rendering html. A container is a way to get your data from the redux store.
That’s why I created AppContainer
:
import React from'react'import{ connect }from'react-redux'import{ addWord }from'../../actions/index'import App from'./App'exportconstAppContainer=props=>(<App addWord={props.addWord} words={props.words}/>)constmapDispatchToProps=dispatch=>({addWord:word=>dispatch(addWord(word)),})constmapStateToProps=state=>({
words: state.words,})exportdefaultconnect(mapStateToProps, mapDispatchToProps)(AppContainer)
Here I added two typical functions for react applications with redux - mapDispatchToProps
&
mapStateToProps
. In the first one, I tell redux that when I call addWord
inside my App
component
it should dispatch an action from actions/index
. The second function is for extracting the data from
the store - it will be the best if my component has only access to this part of a state which it is
concerned about.
That’s all for today! To recap: I’ve added redux to my application and used Presentational and Container Components and I have my debounced input with react & redux!
Github repo can be found here.