React Redux: подключенный дочерний компонент не получает реквизиты

Кажется, я не могу понять, в чем проблема. У меня есть компонент под названием DisplayItems, который получает некоторые данные из API (список элементов).

Я использую действия Redux и промежуточное программное обеспечение thunk для получения элементов и данных из API. Я сохраняю элементы и другие данные в хранилище redux как массив объектов и называю его entriesData. Фактический массив находится в entriesData.data. Я использую connect для подключения состояния и действий к реквизитам DisplayItems.

Затем у меня есть дочерний компонент GalleryItemContentV1. GalleryItemContentV1 не подключен к redux. Он просто получает данные от DisplayItems, который просматривает массив элементов и передает каждый из них GalleryItemContentV1. Итак, снова DisplayItems проходит через this.props.entriesData.data и передает каждый элемент в GalleryItemContentV1 как опору под названием entry.

Наконец, у меня есть дочерний компонент в GalleryItemContentV1 под названием TestCom. Вот в чем проблема. Я также передаю предмет, который я получил от DisplyItem, на TestCom. Итак, поток данных: DisplayItems (подключен)> GalleryItemContentV1 (не подключен)> TestCom (подключен).

TestCom подключен к redux. Он использует некоторые действия из redux для обновления элемента в entriesData.data. Когда я обновляю this.props.entriesData.data в TestCom, GalleryItemContentV1 получает обновленные данные нормально, но TestCom ничего не получает. Пункт в TestCom никогда не обновляется.

Я понял, что проблема в том, когда я подключаю TestCom к redux. Если TestCom не подключен к redux, он также получает обновленные реквизиты, как и GalleryItemContentV1.

Компоненты ниже. Я постарался максимально упростить их.

DisplayItems

import React, {Component} from 'react';
import GalleryItemContentV1 from './GalleryItemContentV1';
import { connect } from 'react-redux';
import {getContestEntriesPageData} from '../../actions/contest';

class DisplayItems extends Component {
  componentDidMount(){
    this.props.getContestEntriesPageData(uri, search);
  }

  render(){

    console.info('DisplayItems props', this.props);

      return (
        <div>
          <div className = "card-deck thumbnails" id = "card-list">
            {  this.props.entriesData.data instanceof Array ? 
                  this.props.entriesData.data.map(function(object, i){
                    return <GalleryItemContentV1 
                      entry = {object} key = {i} 
                      arr_key = {i}
                    />;
                  }, this) : ''
              }
          </div>
        </div>
      )
  }
}

const mapStateToProps = state => (
  {
    entriesData: state.entriesData,
  }
)

const mapDispatchToProps = (dispatch) => {
  return {
    getContestEntriesPageData: (uri, search) => {
      dispatch(getContestEntriesPageData(uri, search));
    }
  }
}

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

ГалереяItemContentV1

import React, { Component } from 'react';
import TestCom from './TestCom';

class GalleryItemContentV1 extends Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
        <div>
        <TestCom entry = {this.props.entry} />
      </div>
    );
    }
}
export default GalleryItemContentV1;

TestCom

Результаты this.props.entry.VotesCount (элемент из DisplayItems) вывожу в TestCom. Вот где возникает проблема. По какой-то причине, когда я подключаю TestCom к redux, его реквизиты не обновляются, но когда он отключается от Redux, его реквизиты обновляются.

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

class TestCom extends Component {
  constructor(props) { 
    super(props);
  }

  render(){

    console.info('TestCom props', this.props);

    return (

      <div>{this.props.entry.VotesCount}</div>

    )
  }
}

const mapStateToProps = state => (
  {
    user: state.user
  }
)
export default connect(mapStateToProps)(TestCom);

Здесь я настраиваю redux, но я не думаю, что это актуально, поскольку GalleryItemContentV1 прекрасно видит обновленные данные.

import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import ContestReducer from '../reducers/contest';

export default function configureStore(initialState) {
  return createStore(
    ContestReducer,
    initialState,
    applyMiddleware(thunk)
  );
}

Итак, подведем итоги.

DisplayItems (подключен, получает обновленные данные из хранилища в порядке)> ГалереяItemContentV1 (не подключен, получает обновления в порядке)> TestCom (подключен, не получает обновления, но получает обновления, если отключен)

Вот мой редуктор, на всякий случай.

import * as ContestActionTypes from '../actiontypes/contest';

const initialState = {
  contest: null,
  subscriptions: [],
  user: null,
  page: null
}
export default function Contest(state=initialState, action) {
  console.info('redux action reducer: ', action)
  switch(action.type) {
    case ContestActionTypes.HANDLE_VOTE:
      const newState = { ...state };
      if (newState.entriesData) {
        const index = newState.entriesData.data.findIndex(x => x.id === action.entry.id)
        newState.entriesData.data[index].VotesCount = action.vote;
      } else {
        newState.entry.VotesCount = action.vote;
      }

      return newState

      default:
        return state;
  }
}

Я также заметил, что то же самое происходит с GalleryItemContentV1. Если я подключу его, он перестанет получать обновления из DisplayItems / хранилища redux.

Любой вклад приветствуется. Спасибо!

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
0
2 021
3

Ответы 3

Вы мутируете свое состояние. Вы делаете неглубокую копию состояния верхнего уровня в этом редукторе среза, но вы не делаете копии всех вложенных уровней внутри. Затем подключенный компонент извлекает state.entriesData, который не изменился по ссылке. Итак, компонент-оболочка connect считает, что состояние вообще не изменилось, и не выполняет повторную визуализацию вашего собственного компонента.

Страница документации Redux "Неизменяемые шаблоны обновления" особо указывает на это как на частую ошибку.

Если постоянное обновление вложенного состояния слишком сложно, я бы посоветовал изучить Библиотека неизменяемых обновлений immer. Кроме того, наш новый redux-starter-kit пакет внутренне использует Immer, чтобы вы могли писать более простые редукторы.

Также см. Мой пост Идиоматический Redux: история и реализация React-Redux для получения информации и подробностей о том, как connect работает внутри.

Да, я тоже думаю, что вы мутируете свое состояние. Ниже представлен корпус TOGGLE_TODO от редуктора. Надеюсь, этот фрагмент кода подойдет для вашего кода.

  case TOGGLE_TODO:
  return Object.assign({}, state, {
    todos: state.todos.map((todo, index) => {
      if (index === action.index) {
        return Object.assign({}, todo, {
          completed: !todo.completed
        })
      }
      return todo
    })
  })

Итак, в вашем случае, если идентификатор совпадает, то обновление выполняется с помощью action.vote. Также читайте о глубоких и неглубоких копиях объектов.

Спасибо за фрагмент кода! Моя проблема заключалась в том, что я выполнял поверхностное клонирование с помощью Object.assign, когда мне следовало выполнять глубокое клонирование. Я не уверен, учитывает ли это опубликованный вами фрагмент кода, но я не думаю, что это так.

Dox 13.12.2018 22:04

это должен быть принятый ответ, parse (stringify)) - довольно дорогостоящая операция, если объект слишком большой.

Yegor Androsov 01.11.2019 10:52

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

Проблема заключалась в том, что я пытался обновить массив внутри объекта (entryData - это объект, а entryData.data - массив), используя обычный метод Object.assign ({}, state, {}), когда мне нужно было использовал JSON.parse (JSON.stringify (состояние)). Мне пришлось прочитать объяснение Mozilla (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign), чтобы понять, что происходит. Я видел решение JSON.parse раньше, но я действительно не понимал мелкого и глубокого клонирования, и, каким бы неглубоким клонированием ни было, я не думал, что я виновен в этом. Оказывается, был.

Вот мой обновленный редуктор, который отлично работает.

case ContestActionTypes.HANDLE_VOTE:
  const newState = JSON.parse(JSON.stringify(state));
  if (newState.entriesData) {
    const index = newState.entriesData.data.findIndex(x => x.id === action.entry.id)
    newState.entriesData.data[index].VotesCount = action.vote;
  } else {
    newState.entry.VotesCount = action.vote;
  }

  return newState;

Другие вопросы по теме