1
votes

I was under the impression that when my Redux store gets updated (via dispatching an action to the reducer), my Provider should make the new store available to all it's child components. So when I connect a container component to the store by using mapStateToProps(), that component should re-render when it receives the new props that go along with the store being updated, without the need for componentWillReceiveProps(). Am I wrong in thinking that?

After several hours of reading docs and other stack overflow answers, something just isn't clicking for me. I'm pretty sure my reducer is working correctly and is returning a new object, my components have access to the store, and my initial state is rendering just fine. If anyone could give me a better idea about the flow of things, I would be forever grateful.

Basically, I have a header component that imports the store, uses Provider and renders a "FAQ" component:

import React from 'react'; 
import FAQ from './Faq';
import {Provider} from "react-redux";
import  store  from '../store/index'

class Header extends React.Component {
    render() {
        return (
                <Provider store = {store}>
                    <FAQ  />
                </Provider>
        )
    }
}
export default Header;

The "FAQ" component is a container that is connected to the store via mapStateToProps(), it imports the "FAQTest" component, which is a presentational component that will get passed this.state.thisFood as props so that it can render the data from the store. There is also an "addFood" function that dispatches my action, which can be seen in mapDispatchToProps() at the bottom.

import React from 'react'; 
import {connect} from 'react-redux';
import FAQTest from './faqTest';

class FAQ extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            thisFood: props.breakfast
        };
    }

    //adding this makes the component state update, but I think it should work without it
    componentWillReceiveProps(nextProps){
        this.setState({thisFood: nextProps.breakfast})
    }

    addFood = () => {
        this.props.addFood();
    }

    render() {
                return (

                    <React.Fragment>
                        <button onClick={this.addFood}> Add Food </button> 
                        <FAQTest food = {this.state.thisFood} />
                        </React.Fragment>
                    )
            }
}



const mapStateToProps = function(state) {
    return {
        breakfast: state.faq.breakfast
    }
}

function mapDispatchToProps(dispatch) {
    return {
        addFood: () => dispatch({type: 'ADD_FOOD', food: 'Waffles'})
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(FAQ);

When I click the "Add Food" button, my store gets updated, and the props of my FAQ container component get updated, because of mapStateToProps(). I thought this would trigger my FAQ component to update its state, however the state does not get updated unless I use componentWillReceiveProps. Is this working as expected?

Just in case I'm doing something silly, here is my reducer:

const initialState = {
    breakfast: ["eggs", "bacon"]
  }

export default function faqReducer(state = initialState, action) {

  switch (action.type) {
    case "ADD_FOOD":
      return Object.assign({}, state, {
        breakfast: [...state.breakfast, action.food]
      })
    default:
      return state;
  }
}

Here is my root reducer with my combineReducers() function:

import { combineReducers } from "redux";
import faq from './faqReducer'

export default combineReducers({
  faq: faq
});
1

1 Answers

1
votes

The problem is that you're copying data from props to state, but only doing that when the component is mounted, and then expecting the state to somehow be updated when new props arrive.

Copying data from props to state is almost always the wrong approach. Please don't do that. Just use the value from props.

Two additional suggestions for improving the code:

  • Prefer using the "object shorthand" form of mapDispatch, rather than writing it as a function
  • We recommend using our new official Redux Starter Kit package as the standard way to write your Redux logic. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once.