Приложение React не реагирует на изменения должным образом. Почему?

близкие. У меня, наверное, нубский вопрос, но все же сниму. Приведенные ниже фрагменты кода довольно просты, и у меня не должно возникнуть проблем, но начнем. Я пытаюсь получить список цветов в компоненте ColorsGrid. Короче говоря, когда пользователь изменяет уровень сложности через раскрывающийся список, должен быть сгенерирован новый набор цветов и, таким образом, отображен. Я думал, что это довольно простое упражнение, но все работает не так, как ожидалось. Всякий раз, когда я меняю сложность, он не реагирует (перерисовывает компонент ColorsGrid), и только после того, как я снова выбираю другой (сложность) уровень, предыдущий вызывает повторную визуализацию. Например, если я выберу «Средний» после начального рендеринга (уровень по умолчанию установлен на «Легкий»), ничего не изменится. Однако, если я вернусь к Легкому (или выберу любую другую сложность), то происходят ли изменения, соответствующие предыдущей (Средней сложности), т.е. ColorsGrid перерисовывает и, таким образом, отображает сетку, соответствующую средней сложности. Что я делаю неправильно?

Ниже приведен соответствующий код.

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

// Get random rgb color
function randomColor() {
  let r = Math.floor(Math.random() * 256);
  let g = Math.floor(Math.random() * 256);
  let b = Math.floor(Math.random() * 256);

  return `rgb(${r}, ${g}, ${b})`;
}

// Set length of color list array as a funciton of difficulty 
function colorsListLength(difficulty) {
  switch (true) {
    case difficulty === 'expert':
      return 25;
    case difficulty === 'hard':
      return 20;
    case difficulty === 'medium':
      return 10;
    default:
      return 5;
  }
}

// Get color list array
function colorsList(colorsLength = 5) {
  const colors = [];

  while (colors.length < colorsLength) {
    colors.push(randomColor());
  }

  return colors;
}

// Set random color to guess from (above) color list array
function randomColorToGuess(colors) {
  const index = Math.floor(Math.random() * colors.length);

  return colors[index];
}

// Set number of game tries as a function of difficulty
function numberOfTries(difficulty) {
  switch (true) {
    case difficulty === 'expert' || difficulty == 'hard':
      return 2;
    case difficulty === 'medium':
      return 1;
    default:
      return 0;
  }
}

// Colors grid component
function ColorsGrid({ difficulty, colorsList }) {
  return (
    <div>
      <strong>Colors Grid</strong>
      <p>Difficulty: {difficulty}</p>
      <div>
        {colorsList.length > 0 ? (
          colorsList.map(color => (
            <div
              style = {{
                backgroundColor: color,
                height: '3rem',
                width: '3rem',
                borderRadius: '50%',
              }}
              key = {color}
            />
          ))
        ) : (
          <div>Loading colors...</div>
        )}
      </div>
    </div>
  );
}

// Main component
class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      difficulty: 'easy',
      colorsList: [],
    };

    this.colorsArray = this.colorsArray.bind(this);
    this.handleChangeEvent = this.handleChangeEvent.bind(this);
  }

  componentDidMount() {
    this.colorsArray(this.state.difficulty);
  }

  colorsArray() {
    const colors = colorsList(colorsListLength(this.state.difficulty));
    const colorToGuess = randomColorToGuess(colors);

    this.setState(() => ({
      colorsList: colors,
      gameTries: numberOfTries(this.state.difficulty),
      colorToGuess,
    }));
  }

  handleChangeEvent(e) {
    this.setState({
      difficulty: e.target.value,
    });

    this.colorsArray(this.state.difficulty); // I was under the impression the (difficulty) state had already been updated here
  }

  render() {
    return (
      <div className = "App">
        <h1>Colors</h1>
        <div style = {{ textAlign: 'right' }}>
          <select
            id = "difficulty"
            value = {this.state.difficulty}
            onChange = {this.handleChangeEvent}
          >
            <option value = "easy">Easy</option>
            <option value = "medium">Medium</option>
            <option value = "hard">Hard</option>
            <option value = "expert">Expert</option>
          </select>
        </div>
        <ColorsGrid
          colorsList = {this.state.colorsList}
          difficulty = {this.state.difficulty}
        />
      </div>
    );
  }
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

Используйте обратный вызов для setState, а внутри обратного вызова вместо this.state используйте state из аргументов reactjs.org/docs/react-component.html#setstate

iofjuupasli 08.04.2019 14:03
setState является асинхронной операцией, поэтому this.state.difficulty может быть установлен или не установлен при вызове this.colorsArray()
miuosh 08.04.2019 14:05
stackoverflow.com/questions/48209452/…
numbers1311407 08.04.2019 14:06

Кроме того, switch(true) в colorsListLength мне кажется запахом кода. Почему бы не просто switch на difficulty?

Robin Zigmond 08.04.2019 14:06

@RobinZigmond Что теперь сказать? Не могли бы вы рассказать об этом подробнее?

Collins Orlando 08.04.2019 14:34

Я просто не понимаю, почему у вас есть switch(true) с падежами, которые имеют форму case difficulty === xxx, когда естественный способ сделать это switch(difficulty) с падежами case 'medium', case 'difficult' и т. д.

Robin Zigmond 08.04.2019 14:39

@RobinZigmond Да, я понял. В основном я использовал переключатель так, как он был в коде. Думаю, это сделало его немного многословным. Спасибо!

Collins Orlando 08.04.2019 14:53
Поведение ключевого слова "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) для оценки ваших знаний,...
1
7
200
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Это потому, что setState() это асинхронный:

setState(newState, callback);

Чтобы получить только что выбранную сложность, вам нужно изменить код следующим образом:

this.setState({
  difficulty: e.target.value,
 }, () => this.colorsArray(this.state.difficulty)
);

Ага! Это работает. У меня была другая версия этого, но что-то не так, и поэтому она выдавала ошибку. Спасибо!

Collins Orlando 08.04.2019 14:26

Проблема заключается в координации ваших вызовов setState. Следующее проясняет ситуацию:

  colorsArray(difficulty) {
    const colors = colorsList(colorsListLength(difficulty));
    const colorToGuess = randomColorToGuess(colors);

    this.setState(() => ({
      difficulty,
      colorsList: colors,
      gameTries: numberOfTries(this.state.difficulty),
      colorToGuess
    }));
  }

  handleChangeEvent(e) {
    this.colorsArray(e.target.value);
  }

Вы можете видеть, что ваш обработчик событий делает один вызов функции обновления цвета. Затем он обрабатывает новые цвета и устанавливает состояние в одном месте.

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