React Rails - Like 按钮状态未正确更新

React Rails - Like button state not updating properly

我有一个供稿,您可以在其中 post 个条目。然后任何用户都可以喜欢这些条目。问题是,在你喜欢一个条目并继续写你自己的条目之后(从而将新条目推到 feed 的顶部),你点击的所有类似按钮的状态将在视觉上被撤消,以便查看显示的正确数据需要手动刷新。

这个问题非常特殊,遗憾的是我没有发现任何与此相关的问题。

这是我的 Like 组件代码(like.es6.jsx):

class Like extends React.Component {
    constructor(props){
        super(props);
        this.state={
            like: this.props.entry.liked,
            likes: this.props.entry.likes_count
        }
        this.post = this.post.bind(this);
        this.delete = this.delete.bind(this);
    }

    post () {
        if(!(this.state.like)){
            $.ajax({
                    method: "POST",
                    url: "/api/v1/likes",
                    data: {
                        log_entry_id: this.props.entry.id
                    },
                })
                .then( (response) => {
                    this.setState({like: true})
                    let likes = this.state.likes;
                    likes++;
                    this.setState({likes: likes})
                });
        }
    }

    delete(){
        if(this.state.like){
            $.ajax({
                    method: "DELETE",
                    url: "/api/v1/likes/" + this.props.entry.id
                })
                .then( (response) => {
                    this.setState({like: false})
                    let likes = this.state.likes;
                    likes--;
                    this.setState({likes: likes})
                });
        }
    }

    render () {
        let show_likes = "";
        if(this.state.like) {
            show_likes = (this.state.likes > 1) ? ("You and " + (this.state.likes - 1) + ((this.state.likes == 2) ? (" other person") : (" others" )) + (" like this.")) : ("You like this.");
        }else if(this.state.likes > 0){
            show_likes = (this.state.likes == 1) ? (this.state.likes + " person likes this.") : (this.state.likes + " people like this.");
        }
        let like_button = this.state.like ?
            <a onClick={this.delete} className=" pull-right"><i className="fa fa-thumbs-o-down"></i> Dislike</a> :
            <a onClick={this.post} className=" pull-right"><i className="fa fa-thumbs-o-up"></i> Like</a>
        return (
            <div>
            <span>
                {show_likes}
            </span>
                {like_button}
            </div>
        );
    }
}

在父组件'log_entry.es6.jsx'中,我按以下方式添加了我喜欢的组件:

<Like entry={entry} key={entry.id} />

如果有更多信息我可以提供,我会很乐意根据要求提供。感谢您花时间查看我的问题!

当您 "continue with writing your own entry" 时,会更新 parent 组件中的 this.state 吗?如果是这样,我敢打赌它会导致 Like 到 re-render 及其道具,这些道具已变成 out-of-date!

这样想:传递给 <Like entry={entry} />entry 只包含初始页面加载时为真的数据。当用户单击 "like" 时,它会更新 Like 组件的 this.date,但 原始 entry更新!

因此,如果某些原因导致 Likeentry 变为 re-render,它将使用陈旧数据呈现!但是,如果您刷新页面,它将获取最新数据并正确呈现。

这听起来像是一个可能的原因吗?

我能想到的最佳解决方案是 container component 模式。在这个模式中,只有一个组件(在树的顶部)有this.state。它将数据作为道具发送到它的 children。只有容器组件发送 AJAX 请求和更新 this.state.

在您的情况下,这意味着将 postdelete 函数移动到 parent 组件,并将它们作为事件处理程序传递给 Like(例如, <Like onLike={this.post} onUnlike={this.delete} ...>) 然后,在 Like 中,您可以将它们用作 on-click 处理程序,例如 onClick={this.onLike}.

虽然这需要仔细组织,但它确实值得,因为每个 render 都是非常可预测的:唯一可以改变的是容器的 this.state,其他所有内容都从 this.props 呈现只有!

我尝试将函数和状态处理程序移动到父组件。然而,事实证明这很麻烦,因为我的“赞”按钮的父级也有一个父级(也许我可以进一步用尽这个方法,但我找到了不同的方法)。

我的解决方案最终是这样的:

class Like extends React.Component {
    constructor(props){
        super(props);
        this.state={
            like: this.props.entry.liked,
            likes: this.props.entry.likes_count
        }
        this.post = this.post.bind(this);
        this.delete = this.delete.bind(this);
    }

    post () {
        if(!(this.state.like)){
            $.ajax({
                    method: "POST",
                    url: "/api/v1/likes",
                    data: {
                        log_entry_id: this.props.entry.id
                    },
                })
                .then( (response) => {
                    this.setState({like: true})
                    let likes = this.state.likes;
                    likes++;
                    this.setState({likes: likes});
                    this.props.entry.liked = true;
                    this.props.entry.likes_count = likes;
                });
        }
    }

    delete(){
        if(this.state.like){
            $.ajax({
                    method: "DELETE",
                    url: "/api/v1/likes/" + this.props.entry.id
                })
                .then( (response) => {
                    this.setState({like: false})
                    let likes = this.state.likes;
                    likes--;
                    this.setState({likes: likes});
                    this.props.entry.liked = false;
                    this.props.entry.likes_count = likes;
                });
        }
    }

    render () {
        let show_likes = "";
        if(this.state.like) {
            show_likes = (this.state.likes > 1) ? ("You and " + (this.state.likes - 1) + ((this.state.likes == 2) ? (" other person") : (" others" )) + (" like this.")) : ("You like this.");
        }else if(this.state.likes > 0){
            show_likes = (this.state.likes == 1) ? (this.state.likes + " person likes this.") : (this.state.likes + " people like this.");
        }
        let like_button = this.state.like ?
            <a onClick={this.delete} className=" pull-right"><i className="fa fa-thumbs-o-down"></i> Dislike</a> :
            <a onClick={this.post} className=" pull-right"><i className="fa fa-thumbs-o-up"></i> Like</a>
        return (
            <div>
            <span>
                {show_likes}
            </span>
                {like_button}
            </div>
        );
    }
}

我在 post 和删除函数上添加了 2 行。

this.props.entry.liked = true;
this.props.entry.likes_count = likes;

this.props.entry.liked = false;
this.props.entry.likes_count = likes;

只是更新此条目的道具。我不确定这是否是最好的解决方案,但对我来说,它帮助我保留了它所属的功能,当然它也可以工作。