Реагирующий компонент: setState работал, но не обновлял компонент

#javascript #reactjs #components

#javascript #reactjs #Компоненты

Вопрос:

извините, я новичок в React. Я пытаюсь создать базовую социальную сеть для изучения react. Контекст: когда я нажимаю на кнопку «мне нравится», setState должен вызвать функцию для обновления состояния моего компонента, но она обновляется только при обновлении страницы. Я думаю, что функция componentDidUpdate вызывается не так, как должна. Что я сделал не так? Спасибо за вашу помощь!

Вот части кода :

Как компонент кнопки:

 class Like_Button extends React.Component {

  constructor(props) {
    super(props);
    this.state = {liked : "Like"};
  }

  isliked(){
    fetch("likes_of_user/")
    .then(res => res.json())
    .then((result) => {
        result.map(x => {if(this.props.pk == x.liked_post){this.setState({liked: "Unlike"});}});
    })
  }

  componentDidMount() {
    this.isliked();
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.liked !== this.state.liked) {
    this.isliked();
  }
}

  render() {
        return (
                <button className = "buttons" onClick={() => {
                var csrftoken = getCookie('csrftoken');
                fetch(`like_post/${this.props.pk}`, {method: "POST", headers: {'Accept': 'application/json', 'Content-Type': 'application/json','X-CSRFToken': csrftoken}})
                }}>{this.state.liked}</button>
            )
    }
}
  

Компонент ленты новостей:

 class Newsfeed_comp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      items: []
    };
  }

  componentDidMount() {
    fetch("get_newsfeed/")
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            isLoaded: true,
            items: result
          });
        },
        (error) => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      )
  }

  render() {
    const { error, isLoaded, items } = this.state;
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
      return <div>Loading...</div>;
    } else {
      return (
        <ul>
          {items.map((item ,index) => (
            <li className="postbox" key={`${item}${index}`}>
              {item.author}
              {item.date}
              {item.content}
              <Like_Button pk={item.id} />
            </li>
          ))}
        </ul>

      );
    }
  }
}
  

Рендеринг ReactDOM:

 ReactDOM.render(<Newsfeed_comp />, document.getElementById("newsfeed_view"))
  

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

1. используйте setState с функцией обратного вызова. this.setState((state) => {…state, загружается: true});

Ответ №1:

Попробуйте что-то вроде этого:

LikeButton.js

 import React, { useEffect, useState } from 'react';

export default function LikeButton({ pk }) {
  const [like, setLike] = useState(false);

  useEffect(() => {
    const fetchLike = async () => {
      const res = await fetch("likes_of_user/");
      const result = await res.json();

      if (result.length > 0) {
        setLike(result.find(item => item.liked_post === pk));
      }
    };

    try {
      fetchLike();
    } catch (error) {
      // handle error
    }
  });

  const handleClick = async () => {
    const csrftoken = getCookie('csrftoken');

    return fetch(`like_post/${pk}`, {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-CSRFToken': csrftoken
      },
      method: 'POST',
    });
  };

  return (
    <button className='buttons' onClick={handleClick}>
      {like}
    </button>
  );
};
  

NewsFeed.js

 import React, { useEffect, useState } from 'react';

export function NewsFeed() {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [items, setItems] = useState([]);

  useEffect(() => {
    const getNewsFeed = async () => {
      const res = await fetch('get_newsfeed/');
      const result = await res.json();

      setIsLoaded(true);
      setItems(result);
    };

    try {
      getNewsFeed();
    } catch (error) {
      setIsLoaded(true);
      setError(error);
    }
  });

  if (error) return <div>Error: {error.message}</div>;
  if (isLoaded) return <div>Loading...</div>;

  const list = items.map((item) => (
    <li className='postbox' key={item.content}>
      {item.author}
      {item.date}
      {item.content}
      <LikeButton pk={item.id} />
    </li>
  ));

  return <ul>{list}</ul>;
};
  

App.js

 ReactDOM.render(<NewsFeed />, document.getElementById('newsfeed_view'));
  

Ответ №2:

Похоже, вы изменили свою логику, т. Е. Ваша кнопка напрямую обновляет данные в серверной части, но ничего не делает для обновления состояния компонента, поэтому componentDidUpdate не вызывается, как вы видели. Требуется обновление, поэтому компонент перемонтируется и componentDidMount может извлекать данные likes.

Попробуйте вместо этого сначала обновить локальное состояние, а затем использовать componentDidUpdate для выдачи побочного эффекта обновления серверной части.

 constructor(props) {
  super(props);
  this.state = { liked: true };
}

isliked() {
  fetch("likes_of_user/")
    .then(res => res.json())
    .then((result) => {
      result.map(x => {
        if (this.props.pk === x.liked_post) {
          this.setState({ liked: false });
        }
      });
  })
}

componentDidUpdate(prevProps, prevState) {
  if (prevState.liked !== this.state.liked) {
    const csrftoken = getCookie('csrftoken');
    fetch(
      `like_post/${this.props.pk}`,
       {
         method: "POST",
         headers: {
           'Accept': 'application/json',
           'Content-Type': 'application/json',
           'X-CSRFToken': csrftoken,
         },
       }
    );
  }
}

<button
  className="buttons"
  onClick={() => this.setState(
    prevState => ({ liked: !prevState.liked })
  )}
>
  {this.state.liked ? "Liked" : "Unliked"}
</button>
  

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

1. Большое спасибо, теперь это работает! Я не уверен, что делает this.setState( prevState => ({ liked: !prevState.liked }) ? Я предполагаю, что это работает this.setState({ liked: false } , но я действительно не понимаю, как. Заранее спасибо!

2. @michael_vincent Он просто переключает логическое значение, т.Е. !true -> false Или !false -> true .