Почему мои данные меняются между предыдущими и новыми при обновлении состояния в ReactJS?

#javascript #reactjs #state

#javascript #reactjs #состояние

Вопрос:

Я работаю над SPA с выборкой данных из API Star Wars. На вкладке символы проекта идея состоит в том, чтобы отображать символы на странице, и вы можете нажать далее или предыдущий, чтобы перейти на страницу 2, 3 и т.д. Это работает, но! имена символов мерцают каждый раз, когда страница меняется, это не происходит после первого щелчка, но если вы продолжаете, это происходит все чаще и чаще.

Я попытался исправить это, очистив состояние перед повторным отображением, но это не работает. Данные сначала извлекаются после монтирования компонента, затем при нажатии btn я использую функцию componentwillupdate для обновления символьного компонента.

Вы можете увидеть компонент здесь: https://github.com/jesusrmz19/Star-Wars-App/blob/master/src/components/Characters.js

И живой проект, здесь: https://starwarsspa.netlify.app/#/Characters

Ответ №1:

посмотрите, решит ли это вашу проблему

 class Characters  extends React.Component { 
constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      webPage: "https://swapi.dev/api/people/?page=",
      pageNumber: 1,
      characters: [],
    };
    this.fetchHero = this.fetchHero.bind(this);
  }



async fetchHero(nextOrPrev) {
    //nextOrPrev values-> 0: initial 1: next page -1:prev page
    let pageNum = this.state.pageNumber   nextOrPrev;
    try {
      const response = await fetch(this.state.webPage   pageNum);
      const data = await response.json();
      const characters = data.results;
      this.setState({
        pageNumber: pageNum,
        characters,
        isLoaded: true,
      });
    } catch (error) {
      this.setState({
        pageNumber: pageNum,
        isLoaded: true,
        error,
      });
    }
  }
  componentDidMount() {
    this.fetchHero(0);
  }

  /*you don't need this-> componentDidUpdate(prevState) {
    if (this.state.pageNumber !== prevState.pageNumber) {
      const fetchData = async () => {
        const response = await fetch(
          this.state.webPage   this.state.pageNumber
        );
        const data = await response.json();
        this.setState({ characters: data.results });
      };

      fetchData();
    }
  }*/
  getNextPage = () => {
    if (this.state.pageNumber < 9) {
      this.fetchHero(1);
    }
  };
  getPrevPage = () => {
    if (this.state.pageNumber > 1) {
      this.fetchHero(-1);
    }
  };
  render(
      // the rest of your code
  )
}
 

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

1. спасибо, что нашли время взглянуть на это. Я взгляну на ваш код, чтобы попытаться понять, что происходит. Спасибо

2. Привет, мистер А, я внес изменения в свой код, и это работает. Это заняло некоторое время, и я прочитал некоторые документы, и теперь я понял, как это работает, но теперь я получаю это сообщение в консоли: Предупреждение: не удается выполнить обновление состояния реакции для размонтированного компонента. Я подумал, что мне не нужно размонтировать компонент, так как я их не удаляю, а просто обновляю. Должен ли я каждый раз их отключать?

3. привет @JesusRamirez, я думаю, вам нужно немного подробнее прочитать о методе жизненного цикла. componentWillUnmount() фактически не отключает компонент. это просто позволяет вам выполнять некоторые действия при отключении компонента. компонент будет размонтирован, даже если вы не используете componentWillUnmount() . подробнее по этой ссылке: buildwithreact.com/article/component-lifecycle

Ответ №2:

Я взглянул на ваш Characters компонент и потратил некоторое время на его рефакторинг, чтобы упростить его.

Вместо того, чтобы делать несколько запросов, я создал функцию, которая выполняет один запрос и при необходимости присваивает состояние. Кроме того, я бы использовал status вместо isLoading , чтобы вы могли лучше отображать содержимое на основе состояния, в котором в данный момент находится ваш компонент.

Кроме того, я использовал useEffect which делает то же самое, что componentDidMount и componentDidUpdate . Затем я использовал там функцию запроса и добавил pageNumber в качестве зависимости, что означает, что запрос будет выполнен только при внесении pageNumber изменений. В вашем случае это изменится, только если вы нажмете предыдущие или следующие кнопки.

Я также упростил ваши getPrev функции и getNext функции страницы. Наконец, я отобразил содержимое страницы на основе статуса.

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

Надеюсь, это поможет. Я бы также посоветовал начать изучать хуки React, поскольку они упрощают использование React way, а также это современный способ разработки приложений React. Я реорганизовал ваш Characters компонент, и если вы хотите, используйте это как пример того, как реорганизовать компоненты класса для React Hooks.

 import React, { useState, useEffect } from "react";
import Character from "./Character";

const baseUrl = "https://swapi.dev/api/people/?page=";

const Characters = () => {
  const [state, setState] = useState({
    characters: [],
    pageNumber: 1,
    status: "idle",
    error: null,
  });
  const { characters, pageNumber, status, error } = state;

  const fetchData = async (pageNumber) => {
    setState((prevState) => ({
      ...prevState,
      status: "fetching",
    }));

    await fetch(`${baseUrl}${pageNumber}`).then(async (res) => {
      if (res.ok) {
        const data = await res.json();

        setState((prevState) => ({
          ...prevState,
          characters: data.results,
          status: "processed",
        }));
      } else {
        setState((prevState) => ({
          ...prevState,
          characters: [],
          status: "failed",
          error: "Failed to fetch characters",
        }));
      }
    });
  };

  useEffect(() => {
    fetchData(pageNumber);
  }, [pageNumber]);

  const getNextPage = () => {
    if (pageNumber !== 9) {
      setState((prevState) => ({
        ...prevState,
        pageNumber: prevState.pageNumber   1,
      }));
    }
  };

  const getPrevPage = () => {
    if (pageNumber !== 1) {
      setState((prevState) => ({
        ...prevState,
        pageNumber: prevState.pageNumber - 1,
      }));
    }
  };

  return (
    <div>
      {status === "failed" ? (
        <div className="charact--container">
          <div className="loading">{error}</div>
        </div>
      ) : null}
      {status === "fetching" ? (
        <div className="charact--container">
          <div className="loading">Loading...</div>
        </div>
      ) : null}
      {status === "processed" ? (
        <div className="charact--container--loaded">
          <h1>Characters</h1>
          <button onClick={getPrevPage}>Prev Page</button>
          <button onClick={getNextPage}>Next Page</button>
          <ul className="characters">
            {characters.map((character, index) => (
              <Character details={character} key={index} index={index} />
            ))}
          </ul>
        </div>
      ) : null}
    </div>
  );
};

export default Characters;

 

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

1. спасибо, что нашли время взглянуть на это. Я посмотрю на хуки и ваш код, чтобы попытаться решить проблему. ценю ваше время