React useEffect выполняет бесконечный цикл

javascript #reactjs

#javascript #reactjs

Вопрос:

Мой эффект использования — получение данных из веб-api. Я хочу отображать все сообщения на домашней странице и снова запускать мой useEffect, когда кто-то создает новое сообщение. Проблема в том, что когда я ставлю зависимость от useEffect, он начинает выполнять бесконечные запросы. Когда я передаю пустой массив как зависимость, когда кто-то создает новое сообщение, оно не отображается на домашней странице, пока я не обновлю страницу. Я много читал в Интернете об этой проблеме, но все же я не знаю, как это сделать. Спасибо

 function App() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const jwt = localStorage.getItem("jwt");

    fetch('https://localhost:44366/api/Posts/getAllPosts',
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          'Authorization': 'Bearer '   jwt
        },
      })
      .then(r => r.json()).then(result => setPosts(result));
  }, [posts]);


  return (
    <div >
      <Router>
        <Header />
        <main className="App">
          {
            posts.map(post => (
              <Post keyToAppend={post.CreatedOn} username={post.User.FirstName} title={post.Title} content={post.Content} images={post.ImageUrls} avatarImage={post.User.MainImageUrl} />
            ))
          }
        </main>
      </Router>
      <Footer />
    </div>
  );
}
 

Post-компонент :

 const Post = ({ keyToAppend, username, title, content, images, avatarImage }) => {
    return (
        <div className="post" key={keyToAppend}>
            <div className="post__header">
                <Avatar
                    className="post__avatar"
                    alt="avatar"
                    src={avatarImage}
                />
                <h3 className="username">{username}</h3>

            </div>
            <h1 className="title">{title}</h1>
            <p className="content">{content}</p>
            <p>
                {typeof images != "undefined" ? <ImageSlider slides={images} /> : ""}
            </p>
        </div>
    )
}


export default Post;
 

Ответ №1:

Удалить posts из массива зависимостей, так что это просто [] . Это приведет к запуску эффекта один раз, при загрузке компонента.

 useEffect(() => {
  const jwt = localStorage.getItem("jwt");

  fetch('https://localhost:44366/api/Posts/getAllPosts',
    {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        'Authorization': 'Bearer '   jwt
      },
    })
    .then(r => r.json()).then(result => setPosts(result));
}, []);
// ^^−−−−− remove `posts` here
 

Причина, по которой он выполняется бесконечно с вашим текущим кодом, заключается в том, что ваш обратный вызов effect изменяет элемент posts состояния, который снова запускает эффект (потому posts что находится в массиве зависимостей).
Вам нужно только включить элементы в массив зависимостей, которые вы прочитали при обратном вызове эффекта. Вы никогда не читаете posts обратный вызов эффекта.


Примечание: этот код становится жертвой fetch API footgun, который я описываю здесь. Перед вызовом необходимо проверить r.ok r.json (и обработать ошибки):

 useEffect(() => {
    const jwt = localStorage.getItem("jwt");

    fetch("https://localhost:44366/api/Posts/getAllPosts", {
        method: "GET",
        headers: {
            "Content-Type": "application/json",
            "Authorization": "Bearer "   jwt
        },
    })
    .then(r => {
        if (!r.ok) { // <=================
            throw new Error(`HTTP error ${r.status}`);
        }
        return r.json();
    })
    .then(result => setPosts(result))
    .catch(error => {
        // ...handle/report error here...
    });
}, []);
 

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

1. Я понимаю все, что вы сказали, кроме «Вам нужно только включить в массив зависимостей вещи, которые вы прочитали в обратном вызове эффекта».. Мне нужно включить в реквизит массива зависимостей, который получает Post component или? Извините, но все равно не понимаю.

2. @koceww — в обратном вызове ничего не используется posts , поэтому вы не должны указывать posts в массиве зависимостей. Подробнее: reactjs.org/docs/hooks-reference.html#useeffect