DEV Community

roggc
roggc

Posted on • Updated on

Use of useReducer to manage state in React 💪

I will show you in this post my experience in React with the use of useReducer. I hope it helps you in your work and learning.
Let's say we have a component like this:

import React from 'react'
import {Div} from './styled'

export default
({state,dispatch})=>
{
  const el=
  <Div>
    {/*component stuff go inside here*/}
  </Div>
  return el
}
Enter fullscreen mode Exit fullscreen mode

Folder structure of our app would be as follows:
--|myapp
    --|src
        --|comps
            --|comp1
                --|index.js
                --|styled.js
                --|state.js
                --|reducer.js
So we have already seen index.js definition. Now let's see how styled.js looks like:

import styled from 'styled-components'
export const Div=
styled.div
`
/*all css for our component goes here*/
`
Enter fullscreen mode Exit fullscreen mode

As you can see we use styled-components library.
reducer.js would be as follows:

export default
(val={},act)=>
{
  switch(act.type)
  {
    case 'COMP1_SOMETHING':
      val=
      {
        ...val,
        any:act.val
      }
      return val
    default:
      return val
  }
}
Enter fullscreen mode Exit fullscreen mode

We have a typical reducer function where we receive an action and give the next state. It's important to note the act.type value which is COMP1_SOMETHING. That's it, all types values must begin with the component name we are defining.
Now let's see state.js definition:

export default
{
  //object definition goes in here
}
Enter fullscreen mode Exit fullscreen mode

Now let's say we have our app component. We are lifting state up to the app component. In our app component we will have the same file structure definition:
--|myapp
    --|src
        --|comps
            --|app
                --|index.js
                --|styled.js
                --|state.js
                --|reducer.js
            --|comp1
                --|index.js
                --|styled.js
                --|state.js
                --|reducer.js
Let's see index.js for the app component:

import React,{useReducer} from 'react'
import {Div} from './styled'
import initialState from './state'
import reducer from './reducer'
import Comp1 from '../comp1/index'

export default
()=>
{
  const [state1,dispatch1]=useReducer(reducer,initialState)
  const [state2,dispatch2]=useReducer(reducer,initialState)
  const [state3,dispatch3]=useReducer(reducer,initialState)
  const [state4,dispatch4]=useReducer(reducer,initialState)
  const el=
  <Div>
    <Comp1 state={state1} dispatch={dispatch1}/>
    <Comp1 state={state2} dispatch={dispatch2}/>
    <Comp1 state={state3} dispatch={dispatch3}/>
    <Comp1 state={state4} dispatch={dispatch4}/>
  </Div>
  return el
}
Enter fullscreen mode Exit fullscreen mode

As we can see we use four times the Comp1 component. So we need to use useReducer four times too in order each of the Comp1 instances have its own state.
Now let's see state.js definition for the app component. It is like this:

import comp1 from '../comp1/state'

export default
{
  comp1
}
Enter fullscreen mode Exit fullscreen mode

And see also reducer.js for the app component. It is like this:

import combine from '../../redux/combineReducers'
import comp1 from '../comp1/reducer'

export default
combine
(
  {
    comp1
  }
)
Enter fullscreen mode Exit fullscreen mode

The styled.js file will be the same as in the comp1 case, only changing CSS inside definition.
It left us define myapp/src/redux/combineReducers.js which is like this:

export default
reducers=>(val={},act)=>
{
  const nextVal = {}
  const keys = Object.keys(reducers)
  for(let i= 0; i< keys.length; i++)
  {
    nextVal[keys[i]]= reducers[keys[i]](val[keys[i]], act)
  }
  return nextVal
}
Enter fullscreen mode Exit fullscreen mode

With these structure we can manage our state through the use of useReducer. To fullfill the example let's see again our myapp/src/comps/comp1/index.js file definition:

import React from 'react'
import {Div} from './styled'
import Something from '../something/index'

export default
({state,dispatch})=>
{
  const toggleSomething=
  e=>
  dispatch({type:'COMP1_TOGGLE_SOMETHING'})
  const el=
  <Div>
    {
      state.comp1.showSomething&& <Something/>
    }
    <button onClick={toggleSomething}></button>
  </Div>
  return el
}
Enter fullscreen mode Exit fullscreen mode

And in our myapp/src/comps/comp1/reducer.js file we have:

export default
(val={},act)=>
{
  switch(act.type)
  {
    case 'COMP1_SOMETHING':
      val=
      {
        ...val,
        any:act.val
      }
      return val
    case 'COMP1_TOGGLE_SOMETHING':
      val=
      {
        ...val,
        showSomething:!val.showSomething
      }
      return val
    default:
      return val
  }
}
Enter fullscreen mode Exit fullscreen mode

etc.
As you can see manage state with useReducer is easy. Key points are combineReducers.js definition and follow always the same structure which I have shown you here.
Let's say we want to define state and reducer also at the app level. We do it like this. In the myapp/src/comps/app/state.js file:

import comp1 from '../comp1/state'

const app=
{
  //state definition for app component
}

export default
{
  comp1,
  app
}
Enter fullscreen mode Exit fullscreen mode

And also we need the reducer definition for the app component which we define in the myapp/src/comps/app/reducer.js file:

import combine from '../../redux/combineReducers'
import comp1 from '../comp1/reducer'

const app=
(val={},act)=>
{
  switch(act.type)
  {
    case 'APP_SOMETHING':
      val=
      {
        ...val,
        any:act.val
      }
      return val
    default:
      return val
  }
}

export default
combine
(
  {
    comp1,
    app
  }
)
Enter fullscreen mode Exit fullscreen mode

If we want to access the state for app component in the myapp/src/comps/app/index.js file we do it by typing state1.app.something for example and so on (the same we did in the comp1 index file definition).

We have had to use four times useReducer because we had four instances of comp1 in the app component. We can then use state1 and dispatch1 resulting from the first call to useReducer to manage state of app component although state2 and state3 and state4 would be valid choices too (always together with its corresponding dispatch function). All of them (state objects) have the same structure, that's it:

{
  comp1:
  {
    //comp1 object state definition
  },
  app:
  {
    //app object state definition
  }
  //other comps definitions
}
Enter fullscreen mode Exit fullscreen mode

I hope this article has helped you in learning and understanding.
Thank you.

Top comments (0)