首页 > 解决方案 > React.js,如何立即呈现用户动作(在等待服务器传播之前)

问题描述

我正在构建一个购物清单网络应用程序。列表中的项目可以切换为“选中”或“未选中”。
我的数据流是这样的:单击项目复选框 --> 发送数据库更新请求 --> 使用新的复选框状态重新呈现列表数据。

如果我完全在反应本地组件状态中处理复选框状态,则用户操作更新非常快。

快速演示:https ://youtu.be/kPcTNCERPAo

但是,如果我等待服务器传播,更新会显得很慢。

慢速演示:https ://youtu.be/Fy2XDUYuYKc

我的问题是:如何使复选框动作立即出现(使用本地组件状态),同时如果另一个客户端更改数据库数据,还更新复选框状态。

这是我的尝试(React 组件):

import React from 'react';
import ConfirmModal from '../ConfirmModal/ConfirmModal';
import {GrEdit} from 'react-icons/gr';
import {AiFillDelete, AiFillCheckCircle} from 'react-icons/ai';
import {MdRadioButtonUnchecked} from 'react-icons/md';
import './ListItem.scss';

class ListItem extends React.Component{
    constructor(props){
        super(props);

        this.state = {
            confirmOpen: false,
            checkPending: false,
            itemChecked: props.item.checked,
        };
    }

    static getDerivedStateFromProps(nextProps, prevState){
        if(nextProps.item.checked != prevState.itemChecked){
            return ({itemChecked: nextProps.item.checked})
        }
        return null;
    }

    render(){
        return (
            <div className={`listItemWrapper${this.state.itemChecked ? ' checked': ''} `}>
                {this.state.confirmOpen ? 
                    <ConfirmModal 
                        triggerClose={() => this.setState({confirmOpen: false})}
                        message={`Do you want to delete: ${this.props.item.content}?`}
                        confirm={() => {
                            this.clickDelete();
                            this.setState({confirmOpen: false});
                        }}
                    /> : null
                }

                <div className="listItem">
                    <div className='listContent'>
                        { this.state.itemChecked ?
                            <strike>{this.props.item.content}</strike>
                            : this.props.item.content
                        }
                        <div className={`editBtn`}>
                            <GrEdit onClick={() => {
                                let {content, category, _id} = this.props.item;
                                this.props.edit({content, category, _id});
                            }}
                            />
                        </div>
                    </div>
                </div>
                <div className={`listToolsWrapper`}>
                    <div className = "listTools">
                        <div onClick={() => this.setState({confirmOpen: true})} className={`tool`}><AiFillDelete className="listItemToolIcon deleteIcon"/></div>
                        <div onClick={() => !this.state.checkPending ? this.clickCheck() : null} className={`tool`}>
                            {this.state.itemChecked ? <AiFillCheckCircle className="listItemToolIcon checkIcon"/> : <MdRadioButtonUnchecked className="listItemToolIcon checkIcon"/>}
                        </div>
                    </div>
                    <div className = "listInfo">
                        <div className ="itemDate">
                            {this.props.item.date}
                            {this.props.item.edited ? <p className="edited">(edited)</p> : null}
                        </div>
                    </div>
                </div>
            </div>
        );
    }

    async clickCheck(){
        this.setState(prevState => ({checkPending: true, itemChecked: !prevState.itemChecked}));
        await fetch(`/api/list/check/${this.props.item._id}`,{method: 'POST'});
        this.setState({checkPending: false});
        //fetch updated list
        this.props.fetchNewList();
    }

    async clickDelete(){
        await fetch(`/api/list/${this.props.item._id}`,{method: 'DELETE'});
        //fetch updated list
        this.props.fetchNewList();
    }
}

export default ListItem;

我对如何在这里正确使用反应生命周期方法感到困惑。我正在使用本地状态来镜像组件道具。我尝试使用 getDerivedStateFromProps() 来同步状态和道具,但这并不能使渲染更快。

标签: javascriptreactjsasynchronousweb-applicationsreact-lifecycle

解决方案


您尝试实现的效果称为Optimistic UI,这意味着当某些异步操作发生时,您正在伪造该操作成功的效果。

例如。Facebook Like 功能 1. 当您在 Facebook 帖子上单击 Like 时,计数会自动增加一(您),然后在后台将 ajax 请求发送到 API。2. 当你从你的 ajax 请求中得到响应时,你会知道它是成功还是失败。如果成功,那么您可以选择什么都不做,但如果失败,您可能希望减少之前添加的点赞数,并向用户显示一些错误(或不显示)。

在您的情况下,您可以做同样的事情,但如果您想要实时更新,您需要创建一些解决方案,如长轮询web sockets .co

此外,您提供的getDerivedStateFromProps看起来不错。


推荐阅读