Claude_Duvier
Claude_Duvier

Reputation: 25

React component : setState worked but didnt update component

sorry i'm new to React. I'm trying to make a basic social network to learn react. Context: When i click on the "like" button, the setState should call the function to update the state of my component, but it is updated only when i refresh the page. I think the ComponentDidUpdate function isn't called like it should. What did i do wrong? Thanks for your help!

Here are the parts of the code :

Like button component:

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>
            )
    }
}

Newsfeed component:

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 render:

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

Upvotes: 2

Views: 73

Answers (2)

Drew Reese
Drew Reese

Reputation: 202618

Looks like you've reversed your logic, i.e. your button directly updates the data in the backend but does nothing to update component state, so the componentDidUpdate isn't called as you've seen. The refresh is required so the component is remounted and the componentDidMount can fetch the likes data.

Try instead to update local state first, then use componentDidUpdate to issue the side-effect of updating the backend.

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>

Upvotes: 1

Ross Sheppard
Ross Sheppard

Reputation: 870

Try something like this:

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'));

Upvotes: 2

Related Questions