как мне создать шаблон react js для следующего API

#javascript #reactjs

#javascript #reactjs

Вопрос:

У меня есть следующий API списка сообщений пользователя

 [
    {
        "id": 24,
        "url": "http://blablabla/api/v1/postdetail/24"
    },
    {
        "id": 23,
        "url": "http://blablabla/api/v1/postdetail/23"
    },
    {
        "id": 22,
        "url": "http://blablabla/api/v1/postdetail/22"
    },
    {
        "id": 21,
        "url": "http://blablabla/api/v1/postdetail/21"
    },
    {
        "id": 20,
        "url": "http://blablabla/api/v1/postdetail/20"
    },
]
 

Я пытался сделать PostList.js
этот файл имеет useEffect, который извлекает Post List API (в результате к API выше)
, а внутри него в JSX есть такой пример кода ниже

PostList.js

 function PostList(props) {
  const [postList, setPostList] = useState([]);
  const [error, setError] = useState('');

  useEffect(() => {
    console.log(props.posts_api);
    axios
      .get(`${props.posts_api}`, {
        headers: header_in_request,
      })
      .then((res) => {
        setPostList(res.data);

        console.log('not errorz');
        console.log(res.data);
        setError(null);
      })
      .catch((err) => {
        console.log(err.message);
        setError(err.message);
      });
  }, []);

  return (
    <React.Fragment>
      {error == '' ? (
        postList.length > 0 ? (
          postList.map(function (post) {
            return (
              // the line where that error had pointed to . after it tried to mount the PostCoba component
              <Post key={post.id} post_api={post.url} />
            );
          })
        ) : (
          <React.Fragment>
            <h2>this user has not posted anything yet</h2>
          </React.Fragment>
        )
      ) : (
        <React.Fragment>
          <h2>{error}</h2>
        </React.Fragment>
      )}
    </React.Fragment>
  );
}
 

the Post.js это файл, который извлекает URL-адрес, который был передан.
и отображает его данные Post

Post.js

 function Post(props) {
  const [postDetail, setPostDetail] = useState({
    id: '',
    poster: {},
    postphoto_set: [],
  });

  const [error, setError] = useState('');

  useEffect(() => {
    axios
      .get(`${props.post_api}`, {
        headers: header_in_request,
      })
      .then((res) => {
        setPostDetail(res.data);

        console.log('not errorz');
        console.log(res.data);
        setError('');
      })
      .catch((err) => {
        console.log(err.message);
        setError(err.message);
      });
  }, []);

  return (
    <React.Fragment>
      {console.log('jsx is running')}
      {error == '' ? (
        <React.Fragment>
          {postDetail.id !== '' ? (
            <React.Fragment>
              <div className="media border p-3" key={postDetail.id}>
                <img
                  src="img_avatar3.png"
                  alt="John Doe"
                  className="mr-3 mt-3 rounded-circle"
                  style={{ width: '60px' }}
                />
                <div className="media-body">
                  <h4>
                    {' '}
                    {postDetail.poster.name}
                    <small>
                      <i>{postDetail.created_at}</i>
                    </small>
                  </h4>
                  <h2>{postDetail.title}</h2>
                  <pre>{postDetail.text}</pre>
                  <p>amp;#9733; {postDetail.likes_amount} likes</p>

                  <div className="row">
                    <div className="col">
                      {postDetail.likebutton == true ? (
                        <input
                          type="image"
                          src={`${process.env.PUBLIC_URL}/icons/like-blue.png`}
                        />
                      ) : (
                        <input
                          type="image"
                          src={`${process.env.PUBLIC_URL}/icons/like-black.png`}
                        />
                      )}
                    </div>
                    <div className="col">
                      <button type="button" className="btn">
                        Comment
                      </button>
                    </div>
                    <div className="col">
                      <button type="button" className="btn">
                        Share
                      </button>
                    </div>
                  </div>
                </div>

                {
                  // showcomment == true amp;amp; (
                  //        <CommentList
                  //            comments_api = {post.comments}
                  //        />
                  //    )
                }
              </div>
            </React.Fragment>
          ) : (
            <React.Fragment>
              <h2>loading..</h2>
            </React.Fragment>
          )}
        </React.Fragment>
      ) : (
        <h2>{error}</h2>
      )}
    </React.Fragment>
  );
}

export default Post;
 

но у меня была эта ошибка (см. Прокомментированный код в PostList.js выше, чтобы узнать строку ошибки)
в нем говорится

Не удается выполнить обновление состояния реакции для размонтированного компонента. Это ошибка, но она указывает на утечку памяти в вашем приложении. Чтобы исправить, отмените все подписки и асинхронные задачи в функции очистки useEffect

есть ли какой-либо лучший шаблон кода для отображения каждого из post API внутри PostList API? или есть какой-нибудь способ исправить ошибку? Спасибо

Ответ №1:

Проблема

Мне потребовалось некоторое время, чтобы заметить вашу ошибку. По сути, все сводится к тому, что в JS, '' != null .

Ваш PostList.js компонент начинается с error состояния, установленного на пустую строку. Затем в вашем useEffect() обратном вызове, который выполняется при монтировании компонента, вы выполняете асинхронный вызов, который завершается успешно и устанавливает состояние массива posts, которое монтирует массив Post.js компонентов.

Пока все хорошо. Но теперь все начинает идти не так.

Когда Post компонент монтируется, у него есть свой собственный асинхронный вызов «при монтировании» useEffect() , который не успевает вернуться раньше…

… выполнение возобновляется при PostList успешном обратном вызове для вызова API. Затем это устанавливает состояние ошибки в null . Это заставляет React планировать повторную визуализацию PostList компонента. Он не запускает эффект снова, но когда он попадает error == '' в логику рендеринга, он оценивается false как равный, и выполнение переходит к предложению else тернарного оператора, что выдает ошибку null . Это также отключает весь массив Post компонентов.

Вскоре после этого возвращаются вызовы API, выполняемые (теперь размонтированными) Post компонентами, и выполняются их успешные обратные вызовы, которые включают setPostDetail() вызов. Но поскольку эти компоненты размонтированы, предупреждение, которое вы видите, регистрируется.

Решение

Немедленное решение — убедиться, что вы вызываете setError('') not setError(null) . Но это не решает реальную проблему здесь, которая заключается в отсутствии отмены запросов API в полете, когда ваши компоненты размонтируются.

Для этого используйте axios CancelToken , который затем можно использовать для отмены запроса (если он еще не завершен) при отключении компонента.

например

   useEffect(() => {
    console.log(props.posts_api);
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();

    
    axios
      .get(`${props.posts_api}`, {
        headers: header_in_request,
        cancelToken: source.token,
      })
      .then((res) => {
        setPostList(res.data);

        console.log('not errorz');
        console.log(res.data);
        setError('');
      })
      .catch((err) => {
        console.log(err.message);
        setError(err.message);
      });
     
      // the important bit: returning a way for React to cancel the request.
      return () => source.cancel();
  }, []);
 

Дальнейшее чтение

Подробнее о том, почему вы всегда должны возвращать функцию очистки из функций эффектов, которые выполняют что-либо асинхронно, см. В документах React для «Эффектов с очисткой».