Я просто пытаюсь научиться использовать избыточность, и у меня есть очень простой компонент списка счетчиков, который имеет список дочерних компонентов счетчика.

У меня есть действие onIncrement на счетчике, которое я хочу увеличить счетчик при нажатии.

Когда я нажимаю инкремент, он обновляет родительское состояние, однако дочерний счетчик не обновляется. Если я просматриваю и затем возвращаюсь к списку, он отображается правильно, что, как мне кажется, означает, что состояние обновилось.

Вот мой код:

Счетчик компонентов

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);

Компонент списка счетчиков

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 {
    componentWillMount() {
        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);

Счетчик действий

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
    })
}

Счетчик редукторов

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;
    }
}
2
user3284707 21 Авг 2018 в 16:19

3 ответа

Лучший ответ

Я предполагаю, что проблема может заключаться в том, что вы присваиваете своим счетчикам [index] тот же старый объект счетчика с обновленным значением count, и, следовательно, компонент Counter не видит изменений. Это может быть из-за того, что shouldComponentUpdate выполняет поверхностную проверку, а ссылка на объект на стойку счетчика остается прежней, а ваш компонент не выполняет повторную визуализацию.

Вы должны использовать глубокое клонирование, если вы используете многомерные массивы или вложенные объекты в своем состоянии.

Рекомендуется использовать библиотеки, такие как Immutable.js, чтобы убедиться, что ваши редукторы остаются чистыми.

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
  };
3
Swati Anand 21 Авг 2018 в 15:40

Вы можете попробовать поместить componentWillReceiveProps в свой компонент Counter и проверять, получаете ли вы новые реквизиты counter каждый раз, когда нажимаете кнопку.

1
bathpp 21 Авг 2018 в 13:57

Вы не отображаете состояние на реквизиты, например:

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

Это отобразит состояние вашего редукса, которое отражает изменение счетчика, на реквизиты вашего компонента, где вы хотите отобразить текущий счетчик. Надеюсь это поможет!

1
Rafael Rocha 21 Авг 2018 в 13:34
51949662