#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. спасибо, что нашли время взглянуть на это. Я посмотрю на хуки и ваш код, чтобы попытаться решить проблему. ценю ваше время