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
}
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*/
`
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
}
}
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 type
s values must begin with the component name we are defining.
Now let's see state.js definition:
export default
{
//object definition goes in here
}
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
}
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
}
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
}
)
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
}
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
}
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
}
}
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
}
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
}
)
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
}
I hope this article has helped you in learning and understanding.
Thank you.
Top comments (0)