React/Redux - Состояние компонента не обновляется

Кажется, я столкнулся с очень распространенной проблемой в React/Redux. Есть множество людей, имеющих именно эту проблему, но причина может сильно различаться, поэтому я не смог найти возможного решения. Я пытаюсь создать веб-страницу со списком, который можно переупорядочить, нажимая кнопки вверх и вниз.

Мне удалось отследить обновленное состояние (массив) до редуктора, но компонент не обновляется. Я считаю, что я не правильно обновляю состояние компонента.

Вот мой компонент:

import React, { Component } from 'react';
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { changeOrder } from "../actions/index.js";

// let list = ["apple", "banana", "orange", "mango", "passion fruit"];

export class Home extends Component {
  constructor(props){
    super(props);
    this.renderList = this.renderList.bind(this);
  }

  goUp(position){
    this.props.changeOrder(position, "up");
  }

  goDown(position){
    this.props.changeOrder(position, "down");
  }

  renderList(fruit){
    const position = this.props.list.indexOf(fruit);
    return (
          <div key={fruit}>
            <li>{fruit}</li>
            <button onClick={() => this.goUp(position)}>Up</button>
            <button onClick={() => this.goDown(position)}>Down</button>
          </div>
        );
  }

  render() {
      return (
        <div>
          <h1>This is the home component</h1>
          <ol>
            {this.props.list.map(this.renderList)}
          </ol>
        </div>
      );
  }
}

function mapStateToProps(state){
  console.log(state);
    return { list: state.list };
}

function mapDispachToProps(dispatch) {
    return bindActionCreators({ changeOrder }, dispatch);
}

export default connect(mapStateToProps, mapDispachToProps)(Home);

Действие:

export const GO_UP = "GO_UP";
export const GO_DOWN = "GO_DOWN";

export function changeOrder(position, direction) {
    console.log("position: " + position + " | direction: " + direction);
   switch (direction) {
       case "up": {
           return {
                type: GO_UP,
                payload: position
           };
       }
       case "down": {
           return {
               type: GO_DOWN,
               payload: position
           };
       }
   }
}

Разбавление:

import { GO_UP, GO_DOWN } from "../actions/index.js";
let list = ["apple", "banana", "orange", "mango", "passion fruit"];

function arrayMove(arr, old_index, new_index) {
    if (new_index >= arr.length) {
        var k = new_index - arr.length + 1;
        while (k--) {
            arr.push(undefined);
        }
    }
    arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
    return arr; // for testing
}

export default function (state = list, action){
    // debugger;
    switch (action.type){
        case GO_UP: {
            let newState = arrayMove(state, action.payload, action.payload -1);
            console.log(newState);
            return newState;
        }
        case GO_DOWN: {
            let newState = arrayMove(state, action.payload, action.payload +1);
            console.log(newState);
            return newState;
        }
        default: return list;
    }
}

Вот Index.js редуктора:

import { combineReducers } from 'redux';
import ReducerList from "./reducer_list";

const rootReducer = combineReducers({
  list: ReducerList
});

console.log(rootReducer);

export default rootReducer;

И, наконец, index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from "redux-promise";

import App from './components/app';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <App />
  </Provider>
  , document.querySelector('.container'));

Любая помощь будет принята с благодарностью!

2 ответа

Решение

Когда вы используете response-redux, вы хотите убедиться, что вы не изменяете состояние, скорее вам нужно создать новый объект состояния (поверхностная копия). Обратитесь сюда для получения дополнительной информации. Redux не будет знать, обновилось ли состояние, если вы изменили предыдущее состояние.

в arrayMove вы изменяете текущее состояние. вместо

arrayMove(state, action.payload, action.payload -1);

используйте следующее,

arrayMove(state.slice(), action.payload, action.payload -1);

Проблема находится в вашем редукторе в функции arrayMove(), Вы меняете существующий массив с именем arr, После его изменения компонент React не знает это свойство state.list на самом деле изменилось, потому что ссылка на память следует за тем же старым массивом (с измененным содержимым).

Чтобы сообщить вашему приложению, что этот список действительно изменен, вам нужно вернуть массив NEW. Как после всей вашей логики в arrayMove, в конце вам нужно вернуть что-то вроде:

return [].concat(arr);

В терминологии функционального программирования ваш arrayMove это грязная функция с побочными эффектами, потому что она мутирует существующий объект / массив по ссылке. "Чистая" функция должна возвращать новый экземпляр объекта.

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