Redux не обновляет состояние правильно - состояние остается пустым массивом, несмотря на добавление объекта в предыдущей строке - PullRequest
0 голосов
/ 03 февраля 2020

Я пытаюсь обновить состояние React Redux, используя обычную практику. Вы постоянно копируете свое текущее состояние в функцию возврата, а затем перезаписываете свойства, которые должны иметь новое значение.

Мой ожидаемый результат -

state.copy

обновить до

[
    { webCopy: "", id: 0 },
    { webCopy: "", id: 1 }
]

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

Я пытался просмотреть результаты поиска StackOverflow для "react redux state not updating correctly" но ни один из результатов поиска не соответствует моей ситуации.

В моем приложении я даже проверил: только

case actionTypes.ADD_COMPONENT

(первый в редукторе) активируется в этой части приложения. Это буквально нажатие кнопки, которое вызывает

ADD_COMPONENT

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

Почему я получаю пустой массив в конце case actionTypes.ADD_COMPONENT?

Мой оператор console.log() даже показывает JavaScript Объект, являющийся значением для newCopy ПРАВО, ДО ТОГО, КАК Я ВОЗВРАЩАЮ новое состояние.

Итак, вот редуктор. js. Я загрузил ПОЛНЫЙ редуктор вместо case actionTypes.ADD_COMPONENT, где происходит ошибка:

import * as actionTypes from "./constants";

let component = "";
let idIteration = null;
let stateArray = [];
let tempCopy = [];

const initialState = {
  components: [],
  uniqueIdCounter: 0,
  currentPage: 1,
  copy: [],
  siteURL: "/salespage/"
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    // you can probably ignore everything outside of this switch statement
    // this is the important switch statement
    case actionTypes.ADD_COMPONENT:
      stateArray = [...state.components];
      console.log("state:", state);
      console.log("State Copy:", state.copy);
      component = action.payload[0]; // will be "Header", "Headline", "Text Area", "Image", "Email Field", "Footer"
      if (component === "Header") {
        // append "Header" component to the beginning of the list
        stateArray.unshift({
          type: component,
          id: state.uniqueIdCounter
        });
      } else {
        // push component to the end of the list
        stateArray.push({
          type: component,
          id: state.uniqueIdCounter
        });
      }
      idIteration = state.uniqueIdCounter + 1;

      // SEND HELP. How could state.copy possibly be anything other than a JS object after these lines?
      let newCopy = [...state.copy];
      newCopy.push({ webCopy: "", id: action.payload[1] });
      console.log("TTTTTTTTT", newCopy);

      return {
        ...state,
        components: stateArray,
        uniqueIdCounter: idIteration,
        copy: newCopy // why doesn't this update the array to contain newCopy? it should.
      };

    // i don't know how any of this could possibly cause my state.copy to be equal to []
    case actionTypes.SET_NEW:
      console.log("Activating SET_NEW");
      // uploads the newly reordered set of components to state
      let uploadNewOrder = [...action.payload];
      return {
        ...state,
        components: uploadNewOrder
      };
    case actionTypes.DEL:
      console.log("activating DEL");
      // uploads the state less the deleted item
      let uploadShortened = [...action.payload];
      return {
        ...state,
        components: uploadShortened
      };
    case actionTypes.PAGE_CHANGE:
      console.log("activating PAGE_CHANGE");

      stateArray = [...state.components];
      return {
        ...state,
        // action.payload is set in each page's ComponentDidMount()
        currentPage: action.payload
      };
    case actionTypes.NEW_VAR:
      console.log("activating NEW_VAR");
      // case "NEW_VAR" fires from Customize's renderStateComponents()
      stateArray = [...state.components];
      tempCopy = Object.assign([], state.copy); // avoids the TypeError bug with [...state.copy]
      // push an empty copy datapoint to state with a unique id to use in identifying which copy goes where in interface
      let newInputFieldNumber = { webCopy: "", id: action.payload };
      tempCopy.push(newInputFieldNumber);
      return {
        ...state,
        components: stateArray,
        copy: tempCopy
      };
    case actionTypes.ADD_COPY:
      console.log("activating ADD_COPY");

      tempCopy = [...state.copy]; // immutably copy state.copy
      let textToAdd = action.payload[0];
      let indexToFind = action.payload[1];

      for (let i = 0; i < tempCopy.length; i++) {
        if (tempCopy[i].id === indexToFind) {
          // Modify the JS object linked to the appropriate input field
          tempCopy[i] = { webCopy: textToAdd, id: indexToFind };
        }
      }
      return {
        ...state,
        components: stateArray,
        copy: tempCopy
      };
    case actionTypes.SET_URL:
      console.log("activating SET_URL");

      stateArray = [...state.components];
      // TODO: handle cases like user entered www.etc.com and https://www.test.com
      let domain = action.payload;
      const notAllowed = [
        "https://www.",
        "www.",
        ".",
        "www",
        "com",
        "net",
        "org",
        ".com",
        ".net",
        ".org"
      ];
      for (let i = 0; i < notAllowed.length; i++) {
        if (domain.includes(notAllowed[i])) {
          domain = domain.replace(notAllowed[i], "");
        }
      }
      return {
        ...state,
        components: stateArray,
        siteURL: "/salespage/" + domain
      };

    default:
      return state;
  }
};

export default reducer;

Есть предложения, что попробовать? Я попытался добавить этот случай к своему редуктору и активировать его в строке после .ADD_COMPONENT, и он все еще дает пустой массив:

case actionTypes.PREP_COPY:

            let prepCopy = [...state.copy];
            prepCopy.push({ webCopy: "", id: action.payload });
            return {
                ...state,
                copy: prepCopy
            };

Я дал переменной-нарушителю newCopy уникальное имя для не использовать глобальную область видимости. В случае, если это как-то важно.

Какой еще код я могу показать? Только редуктор может влиять на состояние Redux, и нет никакого другого кода, кроме .ADD_COMPONENT и (сейчас) .PREP_COPY

EDIT: Согласно советам, я пытался использовать оператор распространения с переменными при возврате состояния. Код теперь работает, если я использую оператор Spread только в ОБА действиях редуктора. Использование его только в одном из них все еще дает пустой массив. Вот так:

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case actionTypes.ADD_COMPONENT:
            // edited out some code...
            let newCopy = [...state.copy];
            newCopy.push({ webCopy: "", id: action.payload[1] });
            console.log("newCopy", newCopy);

            return {
                ...state,
                components: stateArray,
                uniqueIdCounter: idIteration,
                copy: [...newCopy]
            };

        case actionTypes.PREP_COPY:
            console.log("State.copy:", state.copy);
            let prepCopy = [...state.copy];
            prepCopy.push({ webCopy: "", id: action.payload });
            console.log("PREPCOPY:", prepCopy);
            return {
                ...state,
                copy: [...prepCopy]
            };

Так что либо я использую оба эти Действия одновременно, либо нада. Буквально: когда я использую оба, я получаю два JS объекта, добавленных за цикл. Когда я использую только один, я получаю 0 JS объектов, добавленных за цикл. Wut.

Например, я должен сообщить об ошибке команде React?

2-е редактирование: это полностью рабочая песочница кода. Проверьте операторы console.log при нажатии кнопки https://codesandbox.io/s/lucid-heisenberg-iczww

Ответы [ 2 ]

3 голосов
/ 03 февраля 2020

Вы пытались вернуть копию в виде нового массива? Это решило проблемы для меня раньше, чем

Вы можете просто использовать оператор распространения, чтобы создать его:

  return {
    ...state,
    components: stateArray,
    uniqueIdCounter: idIteration,
    copy: [...newCopy]
  };

Другой, аналогичный метод избавит от необходимости помещать значение pu sh во временное переменная:

  return {
    ...state,
    components: stateArray,
    uniqueIdCounter: idIteration,
    copy: [...state.copy, { webCopy: "", id: action.payload[1] }]
  };
2 голосов
/ 03 февраля 2020

Проблема в Pallette.js в вашей ссылке коды и ящики. В строке 29 вы вызываете this.props.copy.pop(), который удаляет последний элемент из copy. props.copy - это тот же массив, что и state.copy, поэтому вы изменяете состояние каждый раз, когда вызывается addComponent. Если вы измените это:

nextCopyId = this.props.copy.pop().id + 1;

на это:

nextCopyId = this.props.copy[this.props.copy.length - 1].id + 1;

или это:

nextCopyId = [...this.props.copy].pop().id + 1;

state.copy больше не является пустым массивом.

Как правило, при работе с React и Redux старайтесь избегать мутативных методов, насколько это возможно. Предпочитаю concat более push или unshift. Предпочитайте map, filter или reduce (которые все возвращают копию массива), а не forEach или for l oop (что побуждает вас изменять массив). Иногда мутация неизбежна, но будьте осторожны и помните, что когда вы мутируете массивы / объекты, поступающие из реквизита, вы также влияете на состояние!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...