javascript - React redux - parent state changing, but child components not re-rendering - Stack Overflow

I am just trying to learn to use redux, and I have a very simple counter list ponent which has a list o

I am just trying to learn to use redux, and I have a very simple counter list ponent which has a list of child counter ponents.

I have an onIncrement action on the counter, which I want to increment the count when clicked.

When I click the increment it updates the parent state, however the child counter does not update. If I browse away and then back to the list, it does render correctly, which seems to me to mean that the state has updated.

Here is my code:

Counter Component

import React, { Component } from "react";
import { connect } from 'react-redux';
import { incrementCounter } from '../../actions/counterActions';
import PropTypes from 'prop-types';

class Counter extends Component {
    render() {
        return <div className="m-2">
            <b>{this.props.counter.count}</b>
            <button className="btn btn btn-secondary btn-sm m-2" onClick={() => { this.onIncrement(this.props.counter) }}>Increment</button>
        </div>;
    }

    onIncrement(counter) {
        this.props.incrementCounter(counter);
    }
}

const mapStateToProps = state => ({
})

Counter.propTypes = {
    incrementCounter: PropTypes.func.isRequired,
}

export default connect(mapStateToProps, { incrementCounter })(Counter);

Counter List ponent

import React, { Component } from "react";
import { RouteComponentProps } from "react-router";
import { CounterContext } from "../../contexts/context.js";
import Counter from "./Counter";
import { NewItem } from "./NewItem";
import ItemContainer from "../layout/ItemContainer";

import { connect } from 'react-redux';
import { getCounters } from '../../actions/counterActions';
import PropTypes from 'prop-types';

class CounterList extends Component {
    ponentWillMount() {
        if (this.props.counters.length == 0) {
            this.props.getCounters();
        }
    }

    render() {
        const counterItems = this.props.counters.map(counter => <Counter key={counter.id} counter={counter} />);
        return <div>{ counterItems }</div>;
    }
}

const mapStateToProps = state => ({
    counters: state.counters.items
})

CounterList.propTypes = {
    getCounters: PropTypes.func.isRequired,
    counters: PropTypes.array.isRequired
}

export default connect(mapStateToProps, { getCounters })(CounterList);

Counter Actions

import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';

export const getCounters = () => dispatch => {
    const counters = [{ id: 1, count: 4 }, { id: 2, count: 3 }, { id: 3, count: 0 }];
    // this could be API call to get initial counters
    console.log('In GetCounters', GET_COUNTERS);

    return dispatch({
        type: GET_COUNTERS,
        payload: counters
    })
}

export const incrementCounter = (counter) => dispatch => {
    // this could be API call to get initial counters
    counter.count++;

    return dispatch({
        type: INCREMENT_COUNTERS,
        payload: counter
    })
}

Counter reducers

import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';

const initialState = {
    items: []
}

export default function (state = initialState, action) {
    console.log(action.type);
    switch (action.type){
        case GET_COUNTERS:
            return {
                ...state,
                items: action.payload
            };
        case INCREMENT_COUNTERS:
            var counter = action.payload;

            const counters = [...state.items];
            const index = counters.findIndex(x => x.id == counter.id);
            counters[index] = counter;

            return {
                ...state,
                items: counters
            };
        default: 
            return state;
    }
}

I am just trying to learn to use redux, and I have a very simple counter list ponent which has a list of child counter ponents.

I have an onIncrement action on the counter, which I want to increment the count when clicked.

When I click the increment it updates the parent state, however the child counter does not update. If I browse away and then back to the list, it does render correctly, which seems to me to mean that the state has updated.

Here is my code:

Counter Component

import React, { Component } from "react";
import { connect } from 'react-redux';
import { incrementCounter } from '../../actions/counterActions';
import PropTypes from 'prop-types';

class Counter extends Component {
    render() {
        return <div className="m-2">
            <b>{this.props.counter.count}</b>
            <button className="btn btn btn-secondary btn-sm m-2" onClick={() => { this.onIncrement(this.props.counter) }}>Increment</button>
        </div>;
    }

    onIncrement(counter) {
        this.props.incrementCounter(counter);
    }
}

const mapStateToProps = state => ({
})

Counter.propTypes = {
    incrementCounter: PropTypes.func.isRequired,
}

export default connect(mapStateToProps, { incrementCounter })(Counter);

Counter List ponent

import React, { Component } from "react";
import { RouteComponentProps } from "react-router";
import { CounterContext } from "../../contexts/context.js";
import Counter from "./Counter";
import { NewItem } from "./NewItem";
import ItemContainer from "../layout/ItemContainer";

import { connect } from 'react-redux';
import { getCounters } from '../../actions/counterActions';
import PropTypes from 'prop-types';

class CounterList extends Component {
    ponentWillMount() {
        if (this.props.counters.length == 0) {
            this.props.getCounters();
        }
    }

    render() {
        const counterItems = this.props.counters.map(counter => <Counter key={counter.id} counter={counter} />);
        return <div>{ counterItems }</div>;
    }
}

const mapStateToProps = state => ({
    counters: state.counters.items
})

CounterList.propTypes = {
    getCounters: PropTypes.func.isRequired,
    counters: PropTypes.array.isRequired
}

export default connect(mapStateToProps, { getCounters })(CounterList);

Counter Actions

import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';

export const getCounters = () => dispatch => {
    const counters = [{ id: 1, count: 4 }, { id: 2, count: 3 }, { id: 3, count: 0 }];
    // this could be API call to get initial counters
    console.log('In GetCounters', GET_COUNTERS);

    return dispatch({
        type: GET_COUNTERS,
        payload: counters
    })
}

export const incrementCounter = (counter) => dispatch => {
    // this could be API call to get initial counters
    counter.count++;

    return dispatch({
        type: INCREMENT_COUNTERS,
        payload: counter
    })
}

Counter reducers

import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';

const initialState = {
    items: []
}

export default function (state = initialState, action) {
    console.log(action.type);
    switch (action.type){
        case GET_COUNTERS:
            return {
                ...state,
                items: action.payload
            };
        case INCREMENT_COUNTERS:
            var counter = action.payload;

            const counters = [...state.items];
            const index = counters.findIndex(x => x.id == counter.id);
            counters[index] = counter;

            return {
                ...state,
                items: counters
            };
        default: 
            return state;
    }
}
Share Improve this question asked Aug 21, 2018 at 13:19 user3284707user3284707 3,3513 gold badges46 silver badges81 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

I guess the problem could be that you are assigning the same old counter object with the updated value of count to your counters[index] and hence the Counter ponent doesn't see a change. This could be because shouldComponentUpdate does a shallow check and the object reference to the counter prop remains the same and your ponent doesn't re-render.

You should be using deep cloning if you are using multi-dimensional arrays or nested objects in your state.

Use libraries like Immutable.js is remended to make sure your reducers remain pure.

case INCREMENT_COUNTERS:
  var counter = action.payload;

  const counters = JSON.parse(JSON.stringify(state.items)); //this creates a deep copy
  const index = counters.findIndex(x => x.id == counter.id);
  counters[index].count = counter.count;

  return {
    ...state,
    items: counters
  };

You're not mapping the state to props, for example:

function mapStateToProps = state => {
  return { count: state.count }; 
}

This will map the state in your redux that reflects the change of the counter to the props of your ponent where you wanna display the current count. Hope this helps!

You could try put ponentWillReceiveProps inside your Counter ponent and check if you are getting the new counter props every time you click the button.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744966066a4603685.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信