Krzysztof Żuraw Blog

Debouncing forms in React with Redux - part three

February 25, 2018

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:

export const ADD_WORD = 'ADD_WORD'

export const addWord = 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'

export const words = (state = [], action) => {
  switch (action.type) {
    case ADD_WORD:
      return [action.word, ...state]
    default:
      return state
  }
}

const rootReducer = combineReducers({ words })
export default 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)
)

export default 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'

export const AppContainer = props => (
  <App addWord={props.addWord} words={props.words} />
)

const mapDispatchToProps = dispatch => ({
  addWord: word => dispatch(addWord(word)),
})

const mapStateToProps = state => ({
  words: state.words,
})

export default connect(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.

Tagged with javascript react redux debounce

This blog post is part of series. Go back to previous one.


Want a monthly digest of these blog posts?

I turned off Disqus comments. If you want to give me feedback please write to krzysztofzuraw(at)fastmail.com or use Keybase.


Krzysztof ŻurawDelivered by Krzysztof Żuraw. Opinions are my own. You can follow updates via RSS feed.