实战升级react hooks

getDerivedStateFromProps +componentDidUpdate 使用方法:

import React from 'react';
import { connect } from 'react-redux';
import { Card, Table, Divider, Modal } from 'antd';
import util from 'scripts/utils/util';
import { operateType } from 'scripts/app/message';
import service from 'scripts/services/auditService';
import sty from '../audit.scss';

class LogTable extends React.Component {
    state = {
        data: [],
        scopeType: this.props.scopeType, //🔴常见错误,props上scopeType的改变并不会触发state中scopeType的改变
        pagination: {
            current: 1,
            pageSize: 10
        },
        filterLogs: this.props.filterLogs,
    }
    componentDidMount() {
        this.searchList();
    }
    static getDerivedStateFromProps(props, state) {
        let data = {}
        if (state.scopeType !== props.scopeType) {
            data["scopeType"] = props.scopeType
        }
        if (JSON.stringify(state.filterLogs) !== JSON.stringify(props.filterLogs)) { //Todo 判断对象是否改变
            data["filterLogs"] = {
                ...props.filterLogs
            }
        }
        return data;
    }
    componentDidUpdate(prevProps, prevState) {
        if (prevState.scopeType !== this.state.scopeType) {
            this.searchList();
        }
        if (JSON.stringify(prevProps.filterLogs) !== JSON.stringify(this.props.filterLogs)) {  //Todo 判断对象是否改变 
            this.searchList();
        }
    }
    searchList(current) {
        if (!this.state.filterLogs) return;
        const { filterLogs } = this.props;
        const { scopeType, pagination } = this.state;
        filterLogs.filterMap.scope = scopeType;
        let filters = {
            ...filterLogs,
            ...pagination,
            current: current || 1
        };
        
        service.searchLoglist(filters).then(res => {
            if (res.success) {
                this.setState({ data: res.data });
            }
        });
        service.loadLoglist(filters).then(res => {
            if (res.success) {
                this.setState({
                    pagination: {
                        ...this.state.pagination,
                        current: current || 1,
                        total: res.data
                    }
                })
            }
        });
    }
    handleChange(pagination) {
        this.setState({
            pagination: {
                ...this.state.pagination,
                current: pagination.current
            }
        })

        this.searchList(pagination.current);
    }
    render() {
        const { data, scopeType, pagination } = this.state;
        const columns = [{
            title: '序号',
            key: 'id',
            render(data, record, index) {
                return (pagination.current - 1) * 10 + index + 1;
            }
        }, {
            title: '操作时间',
            dataIndex: 'operatorTime',
            key: 'operatorTime',
            render(operatorTime) {
                return new Date(operatorTime).format('yyyy-mmon-dd hh:mm');
            }
        }, {
            title: "客户端IP",
            dataIndex: 'ip',
            key: 'ip'
        }, {
            title: '用户昵称',
            dataIndex: 'operatorNickName',
            key: 'operatorNickName'
        }, {
            title: '用户邮箱',
            dataIndex: 'operatorEmail',
            key: 'operatorEmail',
            render(operatorEmail) {
                return operatorEmail == 'null' ? '' : operatorEmail;
            }
        }, {
            title: '操作类型',
            key: 'operateType',
            render(record) {
                return operateType[record.operateType];
            }
        }, {
            title: '操作对象',
            dataIndex: 'targetName',
            key: 'targetName',
            render(targetName) {
                return util.msgType(targetName);
            }
        }, {
            title: '操作结果',
            dataIndex: 'result',
            key: 'result',
            render(result) {
                return result === "SUCCESS" ? "成功" : "失败";
            }
        }]

        return (
            <div>
                <Table
                    columns={columns}
                    rowKey={record => record.operatorTime}
                    dataSource={data}
                    pagination={pagination}
                    onChange={(pagination, filters, sorter) => this.handleChange(pagination, filters, sorter)}
                />
            </div>
        );
    }
}

export default connect(
    state => {
        return {
            filterLogs: state.audit.filterLogs
        }
    }
)(LogTable);
image.png

props属性:
scopeType表示“用户日志”或“管理员日志”
filterLogs表示过滤条件

state属性:
data表示表格的数据源
pagination表示分页信息

当props属性的scopeType和filterLogs发生变化时,需要调用searchList从而更新data和pagination

然而,从上述代码中发现,当scopeType或filterLogs发生变化时,getDerivedStateFromProps和componentDidUpdate会分别执行三次。第一次是由于props属性的变化,而二、三次是由于在searchList中分别调用了两次setState方法,那么setState为什么会触发getDerivedStateFromProps呢?

http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
查阅到上面的生命周期表后,发现,^16.4以后,set­State和forceUpdate也会触发这个生命周期。因此state变化后,又会走 getDerivedStateFromProps 方法,并把 state 值更新为传入的 prop。

react Hooks使用方法:

import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { Table } from 'antd';
import util from 'scripts/utils/util';
import { operateType } from 'scripts/app/message';
import service from 'scripts/services/AuditService';

function LogTable({ scopeType, filterLogs }) {
    const [data, setData] = useState([]);
    const [pagination, setPagination] = useState({ pageSize: 10 });
    const [current, setCurrent] = useState(1);
    const columns = [{
        title: '序号',
        key: 'id',
        render(data, record, index) {
            return (pagination.current - 1) * 10 + index + 1;
        }
    }, {
        title: '操作时间',
        dataIndex: 'operatorTime',
        key: 'operatorTime',
        render(operatorTime) {
            return new Date(operatorTime).format('yyyy-mmon-dd hh:mm');
        }
    }, {
        title: "客户端IP",
        dataIndex: 'ip',
        key: 'ip'
    }, {
        title: '用户昵称',
        dataIndex: 'operatorNickName',
        key: 'operatorNickName'
    }, {
        title: '用户邮箱',
        dataIndex: 'operatorEmail',
        key: 'operatorEmail',
        render(operatorEmail) {
            return operatorEmail == 'null' ? '' : operatorEmail;
        }
    }, {
        title: '操作类型',
        key: 'operateType',
        render(record) {
            return operateType[record.operateType];
        }
    }, {
        title: '操作对象',
        dataIndex: 'targetName',
        key: 'targetName',
        render(targetName) {
            return util.msgType(targetName);
        }
    }, {
        title: '操作结果',
        dataIndex: 'result',
        key: 'result',
        render(result) {
            return result === "SUCCESS" ? "成功" : "失败";
        }
    }];

    useEffect(() => {
        if (!filterLogs) return;
        filterLogs.filterMap.scope = scopeType;
        let filters = {
            ...filterLogs,
            ...pagination,
            current: current || 1
        };

        service.searchLoglist(filters).then(res => {
            if (res.success) {
                setData(res.data);
            }
        });
        service.loadLoglist(filters).then(res => {
            if (res.success) {
                setPagination({
                    ...pagination,
                    current: current || 1,
                    total: res.data
                });
            }
        });
    }, [scopeType, filterLogs, current])

    function handleChange(pageInfo) {
        setCurrent(pageInfo.current)
    }

    return (
        <Table
            columns={columns}
            rowKey={record => record.operatorTime}
            dataSource={data}
            pagination={pagination}
            onChange={(pages) => handleChange(pages)}
        />
    );
}

export default connect(
    state => {
        return {
            filterLogs: state.audit.filterLogs
        }
    }
)(LogTable);

我不再需要去关心props属性的变化状态,不用去做各种判断去控制是否渲染。就像vue里面的computed计算属性,在react中使用useEffect,然后告诉它,帮我盯着scopeType, filterLogs, current这三个变量,只要发生变化,就立刻计算。
同时,我也不需要去写N多this,就像阮大师说的:
React 团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。 组件的最佳写法应该是函数,而不是类。

代码缩减了约50行,一气呵成
惊艳!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352