Marisha
Marisha

Reputation: 196

Component not updating after mapStateToProps fires

I am learning how to implement redux from the ground up, and have run into a problem with my components' re-rendering. My search for this issue on StackOverflow has produced a gazillion results, of which the answer to the question is always "you mutated your state." I have read the connect documentation, I've looked at a bunch of people with similar problems, and I just can't see where state mutation might be the problem here, so I'm going to try asking with my simple example.

Here's my container component:

import React, { Component } from 'react';
import { connect } from 'react-redux';

import { addPokemon } from '../../../redux/actions';
import ReduxTester from '../../components/redux_tester/redux_tester';

export class ReduxTesting extends Component {
    constructor(props) {
        super(props);
    }

    addPokemon(name, pokeType) {
        addPokemon(name, pokeType);
    }

    render() {
        return (
            <div>
                <ReduxTester
                    pokemon={this.props.pokemon}
                    addPokemon={this.addPokemon}
                />
            </div>
        );
    }
}

const MapStateToProps = function(state) {
    return {
        pokemon: state.pokemon,
    };
};

export default connect(MapStateToProps)(ReduxTesting);

Here's my reducer:

const defaultState = {
    pokemon: {},
};

export default function pokemonReducer(state = defaultState, action) {
    const newState = Object.assign({}, state);

    switch (action.type) {
        case 'ADD_POKEMON':
            newState.pokemon[action.name] = action.pokeType;
            break;
        default:
            break;
    }

    return newState;
}

My specific issue is simply that ReactTesting's componentWillReceiveProps method is not firing, and so the component is not being updated and re-rendered. Note that the mapStateToProps method is firing after the action is dispatched. I know this is such a repetitive issue and it's unlikely that my problem is something different, but I just can't find the answer. Any assistance is appreciated.

Edit: For additional clarification, here is my actions.js, where I've imported the dispatch function directly:

import { dispatch } from './store';

// Returns an action that fits into the reducer
export function addPokemon(name, pokeType) {
    dispatch({
        type: 'ADD_POKEMON',
        name,
        pokeType,
    });
}

Edit 2: I've found some additional information, but I don't understand it. It seems that in MapStateToProps, a re-render is triggered when I assign the entire Redux state to one prop - but not when I assign just a portion of the Redux state to prop.

This triggers a re-render:

const MapStateToProps = function(state) {
    return {
        pokemon: state,
    };
};

This does not:

const MapStateToProps = function(state) {
    return {
        pokemon: state.pokemon,
    };
};

Yet I have confirmed my redux store does have the pokemon property and that is where the updates are occurring in the state. Any insight?

Upvotes: 4

Views: 891

Answers (1)

Anas
Anas

Reputation: 5727

Calling your action creator without going through redux won't dispatch the action:

 export class ReduxTesting extends Component {
     constructor(props) {
         super(props);
     }


     addPokemon(name, pokeType) {
         this.props.addPokemon(name, pokeType);
     }

    .
    .
    .


    function mapDispatchToProps(dispatch) {
        return({
            addPokemon: (name, pokeType) => {dispatch(addPokemon(name, pokeType))}
            })
        }

 export default connect(MapStateToProps, mapDispatchToProps)(ReduxTesting);

EDIT 2

You're probably mutating your state, try with something like this:

Reducer

switch (action.type) {
    case 'ADD_POKEMON':
      return {
        ...state,
        pokemon[action.name]: action.pokeType;
      };

Upvotes: 1

Related Questions