Как использовать React setState внутри цикла?

#javascript #reactjs #asynchronous #setstate

#javascript #reactjs #асинхронный #setstate

Вопрос:

У меня есть эта createRoleMember функция, которая обновляет состояние компонента, поэтому React может повторно отображать table () внутри компонента и отображать недавно добавленные roleMembers строки.

   createRoleMember = (newRoleMember) => {
    this.setState((prevState) => {
      return { roleMembers: prevState.roleMembers.concat([newRoleMember]) };
    });
  };
  

Код работает, когда я добавляю один элемент, но если массив содержит более одного значения, createRoleMember функция повторно отображает таблицу с правильным количеством новых строк, но с тем же повторяющимся именем пользователя (всегда дублируя последнее имя пользователя в массиве).

Например, если массив выглядит следующим образом `[«testOne, «TestTwo», «testThree»], таблица будет повторно отображена следующим образом:

 testthree   xxx Aug 25, 2020    
testthree   xxx Aug 25, 2020    
testthree   xxx Aug 25, 2020
  

Вместо:

 testtone    xxx Aug 25, 2020    
testttwo    xxx Aug 25, 2020    
testthree   xxx Aug 25, 2020
  

Если я перезагружаю страницу, в таблице отображаются правильные данные.

Как я могу вызвать createRoleMember (который содержит setState ) внутри цикла?

Больше контекста

createRoleMember Функция вызывается в цикле, который выглядит следующим образом:

   aliasArray.forEach((currAlias) => {
    memberAttributes.alias = currAlias;
    const marshalledObj = AWS.DynamoDB.Converter.marshall(memberAttributes);

    const params = {
      TableName: "xxxx",
      Item: marshalledObj,
    };

    axios
      .post(
        "https://xxxxxx.execute-api.us-west-2.amazonaws.com/xxx/xxx",
        params
      )
      .then(() => {
        createRoleMember(memberAttributes);
        const newRow = document.getElementById(
          `${memberAttributes.alias}-${memberAttributes.Role}`
        );
        newRow.classList.add("new-row");
      });
  });
  
  • aliasArray : массив строк типа «username1», «username2».

  • memberAttributes : объект с данными участника, например, username, grantedBy, grantedOn.

  • newRow : позволяет мне предоставлять обратную связь пользователю (выделяет новую строку зеленым цветом).

Ответ №1:

Я полагаю, что проблема в вашем коде здесь:

 aliasArray.forEach((currAlias) => {
    memberAttributes.alias = currAlias; // Here's the problem

    // ...

    axios
      .post(/*...*/)
      .then(() => {
        createRoleMember(memberAttributes);
        // ...
      });
  });
  

Каждый раз, когда вызывается обратный вызов forEach , вы обновляете значение memberAttributes.alias . Итак, после того, как все члены aliasArray были повторены, значение memberAttributes.alias является последним значением aliasArray .

Итак, когда ваши запросы возвращаются и выполняется успешный обратный вызов ( .then(...) ), memberAttributes.alias это будет последнее значение aliasArray для каждого вызова. Я полагаю, что ваша проблема будет решена путем распространения currAlias до успешного обратного вызова, например:

 aliasArray.forEach((currAlias) => {
    memberAttributes.alias = currAlias;
    const marshalledObj = AWS.DynamoDB.Converter.marshall(memberAttributes);

    const params = {
      TableName: "xxxx",
      Item: marshalledObj,
    };

    axios
      .post(
        "https://xxxxxx.execute-api.us-west-2.amazonaws.com/xxx/xxx",
        params
      )
      .then(() => {
        memberAttributes.alias = currAlias;
        createRoleMember(memberAttributes); 
        const newRow = document.getElementById(
          `${currAlias}-${memberAttributes.Role}` // Modified this line
        );
        newRow.classList.add("new-row");
      });
  });


createRoleMember = (newRoleMember) => {
    this.setState((prevState) => {
      return { roleMembers: prevState.roleMembers.concat([{...newRoleMember}]) };
    });
  };
  

Комментарии:

1. Почему я не могу сделать memberAttributes.alias = currAlias сразу после then ? Я пытался и не сработало, и поскольку это не работает, почему createRoleMember(memberAttributes,currAlias) должно работать? Странная причина, если я console.log(currAlias) получу два журнала с ожидаемыми значениями. Спасибо за вашу помощь, я ценю это!

2. @TheDaniel Хороший момент, вы должны иметь возможность обновлять memberAttributes.alias в then и избавлять себя от изменения подписи createRoleMember . Я полагаю, что здесь есть небольшая ошибка. Вы обновляете, memberAttributes а затем (в createRoleMember ) вы переносили это в состояние. Но вы никогда не переназначаете memberAttributes , поэтому каждый объект в состоянии фактически является ссылкой на один и тот же объект . Я обновил свой ответ, чтобы включить исправление этой проблемы. Обратите внимание, что теперь я использую оператор распространения объекта в createRoleMember для создания нового объекта.