Ertan Hasani
Ertan Hasani

Reputation: 817

Props not updating when redux state change in React Hooks

I am trying to implement Redux on a React Hooks project, but it doesnt seems to work good. Am I doing something wrong here?

reducer.js

const initialState = {
    educations: []
};

export default function home(state = initialState, action){
    switch(action.type){
        case GET_EDUCATIONS: {
            state.educations = action.payload;
            return state;
        }
        default:
            return state;
    }
}

action.js

import * as types from '../constans/home';

export const getEducations = () => {
    return dispatch => {
        const edus = [
            {value: 1, name: 'Bachelor'},
            {value: 2, name: "Master"}
        ]

        dispatch({
            type: types.GET_EDUCATIONS,
            payload: edus
        })
    }
}

component

import React, {useEffect} from 'react';
import {connect} from 'react-redux';
import {getEducations} from '../../redux/actions/home';

function Header({educations, getEducations}) { 
    useEffect(() => {
        getEducations(); //calling getEducations()
    }, [])

    useEffect(() => {
        console.log(educations) //console educations after every change
    })

    return (
        <div className="main-header">
        </div>
    )
}

const mapStateToProps = (state) => {
    return {
        educations: state.home.educations
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        getEducations: () => { dispatch(getEducations())}
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Header);

And the education property in Header function is always an empty array, as in initialState. While when I check on browser with Redux Devtools, it shows that the state contains those two object in array. enter image description here

So no matter if I change the redux state or not, the properties of the component are going to stay as initialState.

Upvotes: 9

Views: 8751

Answers (5)

Michael Nelles
Michael Nelles

Reputation: 5992

I encounter this all the time and resolved it with CLEAR then GET/SET state. This ensures a reset of the state call.

Reducers.js

const initialState = {
    educations: []
};

export default function home(state = initialState, action){
    switch(action.type){
        case GET_EDUCATIONS: {
            return {
               ...state,
               educations: action.payload
            };
        }
        case CLEAR_EDUCATIONS: {
            return initialState;
        }
        default:
            return state;
    }
}

Hooks.js

...
const clearEducation = () => {
  dispatch({ type: CLEAR_EDUCATION });
}
   const getEducations = (payload) => {
      clearEducation(); // this clearing of the state is key fire re-render
      dispatch({ type: GET_EDUCATIONS, payload });
   };

}

Upvotes: 0

Matt Oestreich
Matt Oestreich

Reputation: 8528

It looks like you have solved this while I was getting this typed up - I decided to post it regardless, as it may be helpful.

On top of what Christopher Ngo already mentioned, the following example outlines how you can interact with your store to create new educations and then view them, in separate components..

Edit modest-fermat-98s0r

Cheers!

Upvotes: 0

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15688

In redux, you should avoid directly mutating the state of your reducer. Refrain from doing something like state.reducers = blah. In order for redux to know that you are trying to make an update to state, you need to return a completely new state object. Following these principles, your reducers will update correctly and your components will get the new data.

Reducer.js

const initialState = {
    educations: []
};

export default function home(state = initialState, action){
    switch(action.type){
        case GET_EDUCATIONS: {
            return {
               ...state,
               educations: action.payload
            };
        }
        default:
            return state;
    }
}

In the code above, we return a new state object. It will include everything from the existing state, hence ...state, and we just update the educations property with the action.payload.

Upvotes: 11

Matt Wills
Matt Wills

Reputation: 726

It looks like you’re mutating the state in the reducer. The reducer should always return a new state object if something updated.

You could do what the answers above suggest, but i would recommend using a package like immer (https://www.npmjs.com/package/immer) or immutable.js to prevent any bugs down the line. Using the spread syntax can be dangerous if your state object has some deeply nested properties, and it’s hard to be 100% sure that you haven’t accidentally mutated something, especially as your app grows in size.

Upvotes: 0

Merci Dieu KIMPOLO
Merci Dieu KIMPOLO

Reputation: 443

Can try with the reducer written this way :

const initialState = {
        educations: []
    };

export default function home(state = initialState, action){
    switch(action.type){
        case GET_EDUCATIONS: 
        return {
            ...state, educations:action.payload
        }

        default:
            return state;
    }
}

Upvotes: 1

Related Questions