React的class组件及属性详解!

一、class组件

React 有两种组件:class组件 和 函数组件。class组件需要继承 React.Component,用法如下:

class Welcome extends React.Component {
    render() {
        return <h1>Hello, {this.props.name}</h1>;
    }
}
1、必须要重写的方法

每一个继承 React.Component 的组件,都必须重写 render() 方法。

2、组合而非继承

React建议:不要创建自定义基类组件,使用组合而非继承的方式写组件。


二、组件生命周期
1、方法运行图谱
React的组件生命周期.png

2、挂载

当组件实例被创建并插入 DOM 中时,调用顺序如下:

- constructor()
  • 在组件挂载前被调用,使用方法及注意点如下:

    constructor(props) {
        // 1、一定要写这句,否则会出现  this.props  未定义bug。
        super(props);
    
        // 2、构造函数是唯一能给state初始化的地方,但不要调用 this.setState() 赋值,
        // 会触发render()方法,引起不必要的bug。
        this.state = { counter: 0 };
    
        // 3、这里可以绑定组件的 事件处理函数
        this.handleClick = this.handleClick.bind(this);
    }
    
- static getDerivedStateFromProps()
  • 功能是:根据 props 的变化来更新 state。

    // 初始挂载及后续更新时都会被调用,
    static getDerivedStateFromProps(props, state)
    
- render()
- componentDidMount()
  • 【调用】:会在组件挂载后(插入 DOM 树中)被调用;
  • 【使用】:适合于 数据初始化操作网络请求获取数据操作
  • 【注意】:这里调用 setState(),会触发render(),请谨慎使用,容易导致性能问题。

3、更新

当组件的 props 或 state 发生变化时会触发更新。调用顺序如下:

- static getDerivedStateFromProps()
  • 功能是:根据 props 的变化来更新 state。

    // 初始挂载及后续更新时都会被调用,
    static getDerivedStateFromProps(props, state)
    
- shouldComponentUpdate()

此方法仅用于性能优化。返回true,表示组件需要重新渲染;返回false,表示跳过渲染,默认返回值为 true。

  • 首次渲染或使用 forceUpdate() 时不会调用。

  • state 或 props 改变时,shouldComponentUpdate() 会在渲染执行之前被调用。

  • 不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能。

- render()
- getSnapshotBeforeUpdate()
  • 在组件发生更改之前获取一些信息(譬如:滚动位置等),返回值将作为参数传递给 componentDidUpdate()

    // 函数原型
    getSnapshotBeforeUpdate(prevProps, prevState)
    
    // 使用实例
    class ScrollingList extends React.Component {
        constructor(props) {
            super(props);
            this.listRef = React.createRef();
        }
    
        getSnapshotBeforeUpdate(prevProps, prevState) {
            // 我们是否在 list 中添加新的 items ?
            // 捕获滚动位置以便我们稍后调整滚动位置。
            if (prevProps.list.length < this.props.list.length) {
                const list = this.listRef.current;
                return list.scrollHeight - list.scrollTop;
            }
            return null;
        }
    
        componentDidUpdate(prevProps, prevState, snapshot) {
            // 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
            // 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
            //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
            if (snapshot !== null) {
                const list = this.listRef.current;
                list.scrollTop = list.scrollHeight - snapshot;
            }
        }
    
        render() {
            return (
                <div ref={this.listRef}>{/* ...contents... */}</div>
            );
        }
    }
    

- componentDidUpdate()
  • 组件更新后会被调用,首次渲染不会执行此方法。

  • 可以执行一些自定义操作,譬如进行一些网络数据请求。

    componentDidUpdate(prevProps) {
        // 典型用法(不要忘记比较 props):
        if (this.props.userID !== prevProps.userID) {
            this.fetchData(this.props.userID);
        }
    }
    
  • 可以调用 setState(),但是一定要用条件语句包裹 setState(),否则渲染会进入死循环,因为setState会触发render(),render()后又会调用componentDidUpdate。请谨慎使用。

  • 如果 shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()。


4、卸载
- componentWillUnmount()
  • 当组件从 DOM 中移除时(卸载及销毁之前)调用。
  • 在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。

5、错误处理

当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:

- static getDerivedStateFromError()
  • 在渲染阶段,后代组件抛出错误后被调用。

    // 函数原型
    static getDerivedStateFromError(error)
    
    // 使用示例
    class ErrorBoundary extends React.Component {
        constructor(props) {
            super(props);
            this.state = { hasError: false };
        }
    
        static getDerivedStateFromError(error) {
            // 更新 state 使下一次渲染显示自定义错误UI
            return { hasError: true };
        }
    
        render() {
            if (this.state.hasError) {
                // 你可以渲染任何自定义的  UI
                return <h1>Something went wrong.</h1>;
            }
    
            return this.props.children;
        }
    }
    

- componentDidCatch()
  • // 后代组件抛出错误后被调用,可用于写错误日志

    // 函数原型
    componentDidCatch(error, info)
    // error : 抛出的错误;
    // info  : 错误的堆栈信息
    
    // 使用示例
    class ErrorBoundary extends React.Component {
        constructor(props) {
            super(props);
            this.state = { hasError: false };
        }
    
        static getDerivedStateFromError(error) {
            // 更新 state 使下一次渲染可以显示降级 UI
            return { hasError: true };
        }
    
        componentDidCatch(error, info) {
            // "组件堆栈" 例子:
            //   in ComponentThatThrows (created by App)
            //   in ErrorBoundary (created by App)
            //   in div (created by App)
            //   in App
            logComponentStackToMyService(info.componentStack);
        }
    
        render() {
            if (this.state.hasError) {
                // 你可以渲染任何自定义的降级 UI
                return <h1>Something went wrong.</h1>;
            }
    
            return this.props.children;
        }
    }
    

6、属性
- defaultProps
  • 为 props 添加默认值。

    class CustomButton extends React.Component {
        // ...
    }
    
    CustomButton.defaultProps = {
        color: 'blue'
    };
    
    render() {
        return <CustomButton />; // props.color 将设置为 'blue'
    }
    
- displayName
  • 字符串类型,多用于调试消息。
- props
  • 组件的内置属性,可用于组件间的属性数据传递。this.props.children :特指子组件。详细用法,看这里!
- state
  • 组件内置属性,它是一个普通 JavaScript 对象,用于组件内表示随时会发生变化的数据。详细用法,看这里!

7、其他
- setState()
  • 函数原型

    setState(updater, [callback])
    
  • updater:如下两种使用方式

    // 用函数方式:
    this.setState((state, props) => {
        return { counter: state.counter + props.step };
    });
    
    // 用对象方式:
    this.setState({ quantity: 2 })
    
  • callback参数: 组件更新完成后进行的回调,不建议使用,应该把操作放在 componentDidUpdate() 中更合适。


- forceUpdate()
  • 强制调用 render() 进行重新渲染,会跳过 shouldComponentUpdate(),但其子组件会不会跳过。通常应该避免使用此方法。

    // 函数原型
    component.forceUpdate(callback)
    

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

推荐阅读更多精彩内容