【问题标题】:Parent component rerender useEffect父组件重新渲染 useEffect
【发布时间】:2020-01-03 14:45:12
【问题描述】:

我有一个名为 Posts 的父组件,它有一个 useEffect 并获取数据,然后我有一个名为 Post 的子组件,它从 props 呈现帖子。我在 Post 中有一个删除和喜欢的功能,我希望父级在帖子被点赞或帖子被删除时重新呈现。我怎样才能做到这一点?

const Posts = () => {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    getPosts();
  }, []);

  const getPosts = async () => {
    const data = await fetch('https://localhost:1111/api/posts/posts', {
      credentials: 'include'
    });
    const response = await data.json();
    console.log(response);
    setPosts(response);
  };

  return (
    <div>
      {posts.map(post => (
        <Post key={post.id} post={post} />
      ))}
    </div>
  );
};

const Post = props => {
  const like = async post => {
    const formData = new FormData();
    formData.append('id', post);
    const data = await fetch('https://localhost:1111/api/posts/like', {
      method: 'POST',
      body: formData,
      credentials: 'include'
    });

    const response = await data.json();
    console.log(response);
  };

  const deletePost = async post => {
    const formData = new FormData();
    formData.append('id', post);
    const data = await fetch('https://localhost:1111/api/posts/deletepost', {
      method: 'POST',
      body: formData,
      credentials: 'include'
    });

    const response = await data.json();
    console.log(response);
  };

  return (
    <div>
      <img
        src={`http://localhost:1111/api/posts/uploads/images/${props.post.content}`}
        alt={`Post ${props.post.id}`}
      />
      <button onClick={() => like(props.post.id)}>Like</button>
      <button onClick={() => deletePost(props.post.id)}>Delete</button>
    </div>
  );
};

【问题讨论】:

  • 为什么要重新渲染父帖子?重新渲染子 Post 还不够吗?
  • 因为父 Posts 是获取数据的组件,我想实时更新点赞和删除

标签: reactjs


【解决方案1】:

我建议您在子 &lt;Post /&gt; 组件上添加一个额外的 prop 方法 (onUpdate),每当触发 deletePost()like() 时都会触发该方法。

const Posts = () => {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    getPosts();
  }, []);

  const getPosts = async () => {
    const data = await fetch('https://localhost:1111/api/posts/posts', {
      credentials: 'include'
    });
    const response = await data.json();
    console.log(response);
    setPosts(response);
  };

 const handleUpdate = () => {
   getPosts();
 }

  return (
    <div>
      {posts.map(post => (
        <Post key={post.id} onUpdate={() => handleUpdate()} post={post} />
      ))}
    </div>
  );
};

const Post = props => {
  const { onUpdate } = props;

  const like = async post => {
    const formData = new FormData();
    formData.append('id', post);
    const data = await fetch('https://localhost:1111/api/posts/like', {
      method: 'POST',
      body: formData,
      credentials: 'include'
    });

    const response = await data.json();
    console.log(response);

    onUpdate();
  };

  const deletePost = async post => {
    const formData = new FormData();
    formData.append('id', post);
    const data = await fetch('https://localhost:1111/api/posts/deletepost', {
      method: 'POST',
      body: formData,
      credentials: 'include'
    });

    const response = await data.json();
    console.log(response);

    onUpdate();
  };

  return (
    <div>
      <img
        src={`http://localhost:1111/api/posts/uploads/images/${props.post.content}`}
        alt={`Post ${props.post.id}`}
      />
      <button onClick={() => like(props.post.id)}>Like</button>
      <button onClick={() => deletePost(props.post.id)}>Delete</button>
    </div>
  );
};

如您所见,onUpdate()deletePost()like() 的末尾都被调用,而在父 Posts 组件上,handleUpdate() 将被调用,进而提交 fetch 响应以更新posts 状态。这将导致父 &lt;Posts /&gt; 重新渲染。

【讨论】:

    【解决方案2】:

    在父组件中保留发布功能并将更新功能传递给Post

    const Posts = () => {
      const [posts, setPosts] = useState([])
    
      useEffect(() => {
        fetch('https://localhost:1111/api/posts/posts', {
          credentials: 'include',
        })
          .then(res => res.json())
          .then(setPosts)
      }, [])
    
      const likePost = async postId => {
        await fetch...
        setPosts(prevState =>
          prevState.map(post =>
            post.id === postId
              ? {
                  ...post,
                  likes: post.likes + 1,
                }
              : post
          )
        )
      }
    
      const deletePost = async postId => {
        await fetch...
        setPosts(prevState => prevState.filter(post => post.id !== postId))
      }
    
      return (
        <div>
          {posts.map(post => (
            <Post
              key={post.id}
              post={post}
              likePost={likePost}
              deletePost={deletePost}
            />
          ))}
        </div>
      )
    }
    
    const Post = ({ post, likePost, deletePost }) => (
      <div>
        <img
          src={`http://localhost:1111/api/posts/uploads/images/${post.content}`}
          alt={`Post ${post.id}`}
        />
        <button onClick={() => likePost(post.id)}>Like</button>
        <button onClick={() => deletePost(post.id)}>Delete</button>
      </div>
    )
    

    【讨论】: