10bet:关于 react 那些小知识点儿

来源:http://www.chinese-glasses.com 作者:Web前端 人气:64 发布时间:2020-03-14
摘要:时间: 2019-12-01阅读: 81标签: react 众所周知, React是通过管理状态来实现对组件的管理,而setState是用于改变状态的最基本的一个方法,虽然基础,但是其实并不容易掌握,本文将结合部

时间: 2019-12-01阅读: 81标签: react

10bet 1

众所周知, React 是通过管理状态来实现对组件的管理,而setState是用于改变状态的最基本的一个方法,虽然基础,但是其实并不容易掌握,本文将结合部分源码对这个方法做一个相对深入的解析。

react.jpg

基本用法

说在前面

关于 react 的总结过去半年就一直碎碎念着要搞起来,各(wo)种(tai)原(lan)因(le),没错就是我太懒了。上个月去体检的时候,查体检报告时都顶着各种心理压力呢, 和想象中的一样各种职业病各种身体亚健康呢。 所以现在尽量早睡坚持早起了,然后呢,起得早就有时间做点儿总结了。心心念的东西重新拿了起来了。希望这个总结归纳能对你的日常开发或者跳槽面试有帮助哪怕只有那么一点点,反正对我帮助是挺大,温故而知新嘛!!!

废话说了一堆, 这个总结可能大概也许会以问答的形式总结
希望你能各个击破,像闯关卡一样一个一个过!开始吧 !Let's go!

首先官方文档给出的,它的基本API:

【1】react component有几种写法?分别是什么?

① 函数式定义的无状态组件(Stateless Functional)

  • 性能更高效、代码更简洁
  • 没有 state,也就是无状态
  • 不需要管理/维护 组件的生命周期
  • 纯函数,相同的 props 会得到同样的UI渲染结果
    function List (props) {
        return <div>我是一个函数式定义的react组件</div>
    }

ES5方式 React.createClass 定义的组件(该方式已经被废弃,官方文档推荐是用①和③)
③ ES6 方式定义的组件(Class Components)

    class List extends React.Component {
        render() {
            return <div>我是一个es6方式定义的react组件</div>
        }
    }

官方文档写的还是颇具神秘感的,先告诉我们①和③方式在 UI 渲染效果是一毛一样的,但是'Classes have some additional features...' Class 这种方式比 Functional 这种方式多了些不一样的地方。那么问题来了。多了哪些不一样的呢? 不一样的地方你可能也发现了,有无 state有无生命周期...等

// 接受2个参数,updater 和 callbacksetState(updater[, callback])// 第一种:updater可以是对象setState({ key: val}, newState={ // callback里可以获取到更新后的newState})// 第二种:updater可以是函数,返回一个对象值setState((state, props)={ return { key: val }}), newState={})
【2】那什么时候该用 Stateless Functional 什么时候用 Class 呢?

推荐使用 Functional,能用 Functional 就用 Functional,就酱。
多说一句。Class 是用来创建包含状态生命周期用户交互的复杂组件,而当组件只是用来纯展示或者 props 传递渲染时(展示性),二话不说请用 Stateless Functional 来快速创建组件。

其中updater表示新的state值可以是返回一个对象的函数,也可以直接是一个对象。这部分的内容会通过浅比较被合并到state中去。官方文档很明确的告诉我们:

【3】无状态组件(Stateless Functional)有哪些优缺点
  • 优点
  1. 语法/代码更加简洁
  2. 占用内存小(没有 props 等属性), 首次 render 性能更好
  3. 不需要管理/维护组件的生命周期
  4. 纯函数,相同的 props 会得到同样的 UI 渲染结果
  5. 单元测试更容易进行。因为逻辑都被移出了 view 层,所以单元测试时不需要渲染任何东西,可以专注于单个逻辑。
  • 缺点
  1. 无生命周期函数。对于一个函数而言应该是谈不上生命周期。当然了,我们其实可以使用高阶组件去实现生命周期。
  2. 没有 this。在Stateless中 this 是 undefined 。

setState 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。将 setState() 视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。

【4】React.Component 绑定方法的几种方法?
    //第一种方法:构造函数中绑定

    class List extends React.Component {
        constructor(props) {
            super(props)
            this.onClickList = this.onClickList.bind(this)
        }

        onClickList() {
            console.log('我被点了')
        }

        render() {
            return <div onClick={this.onClickList}>点我点我点我</div>
        }

    }

    //第二种方法: 在render()行内事件使用bind来绑定

    class List extends React.Component {

        onClickList() {
            console.log('我被点了')
        }

        render() {
            return <div onClick={this.onClickList.bind(this)}>点我点我点我</div>
        }

    }

    //第三种方法: 使用箭头函数 => 

    class List extends React.Component {

        onClickList = () => {
            console.log('我被点了')
        }

        render() {
            return <div onClick={this.onClickList}>点我点我点我</div>
        }

    }

    //第四种,当然,你要在render()行内使用箭头函数也行

    class List extends React.Component {

        onClickList() {
            console.log('我被点了')
        }

        render() {
            return <div onClick={() => this.onClickList()}>点我点我点我</div>
        }

    }

我日常开发都比较喜欢用箭头函数的方法,就因为偷懒,代码量比第一种少。当然,官方说在 render 中创建函数(第二,和第四种)可能会有性能问题。但往往需要传递参数或者回调时,都得用到。例如:

    <button onClick={() => this.handleClick(id)} />
    <button onClick={this.handleClick.bind(this, id)} />

因此这个api的第二个参数callback,允许我们在setState执行完成后做一些更新的操作。

【5】智能组件 vs 木偶组件 ? 或者 容器组件 vs 展示组件 ?

Smart 组件 和 Dumb 组件对于开发过 react 项目的朋友来说应该不陌生了。

Dumb 组件,听名字你就知道这种组件很傻很木,因为木偶组件只关心一件事情就是 —— 根据 props 进行渲染。
Smart 组件10bet,就很聪明,它专门做数据相关的逻辑,和各路数据打交道,ajax获取数据,定义好数据操作的相关函数,然后将这些数据、函数直接传递给具体实现的组件(Dumb 组件)即可。所以根据是否需要高度的复用性,把组件划分为 Dumb 和 Smart 组件。

小提示1:Smart 组件复用性不强甚至不需要复用,Dumb 组件往往是复用性强的,但是Dumb 组件对于 Smart 组件的带入性不要太强,因为带入太多逻辑会导致复用性降低。二者选择,设计组件时需要斟酌一下。

小提示2:Dumb 组件 的子组件也应该是 Dumb 组件。

以上稍微回顾下基础知识部分,接下来我们正式开始详细探讨。

关于React生命周期

关于生命周期,面试的时候总喜欢问点儿相关的,而且想要了解别人写的 react 代码,深刻理解 react 生命周期也是很重要的。先不要往下看,闭上眼睛想想看你所了解的 react 生命周期有哪些?

...

...

...

...

...

...

10bet 2

liftcycle.jpg

ok 你应该想完了哈。是不是大概有下面这么一个流程?(图片是经典的组件挂载图来源于网络)

react 组件的生命周期方法都可以被分割成四个阶段:初始化、挂载阶段(Mounting)、更新阶段(Updating)、卸载阶段(Unmounting)。

接下来就让我们去看看生命周期都有哪些小知识点。

关于第一个函数参数

【6】Mounting(挂载) -- 下面这些方法将会在 component 实例被创建和插入到DOM后调用。
  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

为了避免枯燥,我们带着问题来继续研究:

【7】Updating -- props 或者 state 的变化都会导致更新。下面这些方法会在 component 重新渲染时调用。
  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate()

问题1:setState使用函数参数和对象参数有何区别?在回答这个问题之前,请先看这个很常用的计时器的例子:

【8】Unmounting -- 该方法将会在 component 从DOM中移除时调用。
  • componentWillUnmount()
class Demo extends Component { constructor(props) { super(props); this.state = { count: 0 //初始值 }; } increaseCount = () = { this.setState({count: this.state.count + 1}); } handleClick = ()={ this.increaseCount(); } render() { return ( div className="App-header" button onClick={this.handleClick}点击自增/button count:{this.state.count} /div ); }}

接下来简单的介绍一下几个生命周期。

这个代码看起来没有什么问题, 每次点击的时候也能自增1,完全符合预期效果。但是接下来!我们希望通过改动handleClick,使得每次点击时,count自增2次,即:

【9】1. componentWillMount

componentWillMout() 是在组件挂载(mount)之前被调用.
componentWillMount()是唯一一个在服务器端渲染(ssr)调用的生命周期钩子

关于 setState 在 componentWillMount 使用:可以使用。因为它是 render 方法之前被调用,因此 setState 也不会导致重绘(re-render)

 handleClick = ()={ this.increaseCount() this.increaseCount() }
【10】2. componentDidMount

componentDidMount() 在组件挂载之后立即执行

在这个钩子里合适:

  • ajax 请求
  • 初始化DOM节点的操作
  • 设置计时器 setTimeout 或者 setInterval (温馨提示,别忘了在 componentWillUnmount 关闭这些计时器)

关于 setState 在 componentDidMount 使用: 可以使用。但是经常导致性能问题。当然非要在 render 前拿到 DOM 节点的大小和位置,是可以用的。

插曲。面试题:ajax 请求应该在哪个生命周期?为什么?

这时候就会惊奇地发现,每次点击后,count还是自增1!问题出在哪里呢?

【11】3. componentWillReceiveProps(nextProps)

componentWillReceiveProps 将会在已挂载组件(mounted component)接收到新的 props 之前调用。所以初始化 props 的mount不会触发这个函数。直接 setState不会触发这个函数。

在这个钩子里合适:

  • 更新 state 的值(比如重置)
  • 比较 this.props 和 nextProps

特别特别特别要注意的是,当父组件导致该组件 re-render 时,即便 props 没有发生任何的改变,react 也有可能执行该钩子函数。所以呢,所以就是如果你想要真正处理 props 的变化,要记得比较当前 props 和 nextProps.

关于setState在componentWillReceiveProps使用: 可以使用

其实就是this.setState({count: this.state.count + 1});这种写法,由于前面提到了setState并非同步方法,所以这里的this.state.count并不能保证取到最新的值,这时候我们可以采用第二种写法:

【12】4. shouldComponentUpdate(nextProps, nextState)

当改变state 或者 props 并且是在render之前会调用shouldComponentUpdate,说白了就是该钩子函数用于告诉 React 组件是否需要重新渲染。

shouldComponentUpdate 默认return true,如果return false componentWillUpdaterendercomponentDidUpdate都将不会被调用。千万记住一点, 当return false时,当他们的 state 发生改变时,并不会阻止子组件(child component)进行重新渲染。

shouldComponentUpdate在两种情况下不会被调用:

  • 组件初始化
  • 使用forceUpdate的情况

大家应该都是 shouldComponentUpdate 还有一个知识点就是和 react 组件性能优化相关的。是的。你可以this.state 和 nextState、this.props 和 nextProps 做比较来决定出 return false 并告诉 react 可以不更新该组件。如果做的只是一些浅层的数据比较完全可以用 PureComponent 来代替(深层的嵌套数据PureComponent也无能为力)

react 不建议在 shouldComponentUpdate 做深层的对比或者用 JSON.stringify(),因为这样反而损害到性能。

 // 这里我们用的函数参数的方式 increaseCount = () = { this.setState((state)={return{count: state.count + 1}}); }
【13】5. componentWillUpdate(nextProps, nextState)

state 或者 props 更新后 re-render 之前调用。

注意:不要在componentWillUpdate 使用 this.setState, 或者 redux 分发一个action(dispatch a Redux action),因为在 componentWillUpdate 之前会触发组件的更新。 如果非要在做以上操作的话,可以在componentWillReceiveProps 哦

这时候再试试,发现计时器可以按照预期效果执行,此时可以回答问题1了:如果setState时需要根据现有的state来更新新的state,那么应该使用函数参数来保证取到最新的state值。

【14】6. componentDidUpdate(prevProps, prevState)

在组件更新之后马上调用 componentDidUpdate。

在这个钩子函数中你可以:

  • 操作 DOM
  • 发起网络请求

答案1: 如果需要依赖当前state的值来更新下一个值的情况,需要使用函数作为参数,因为函数才能保证取到最新的state

【15】7. componentWillUnmount

在组件卸载(unmounted)和销毁(destroyed)前调用。

在componentWillUnmount你可以执行任何需要清除的方法。比如:

  • 清除计时器
  • 断开网络请求
  • 解绑dom事件
  • 等等

关于批量更新

【16】生命周期table
生命周期 是否可以调用this.setState 初始化是否执行
componentWillMount 可以
componentDidMount 可以
componentWillReceiveProps 可以
shouldComponentUpdate 不可以
componentWillUpdate 不可以
componentDidUpdate 可以
componentWillUnmount 不可以

特别特别特别注意:
①componentWillMount 和 componentWillReceiveProps 调用 setState 不会重复渲染(re-render)
②componentDidUpdate,不能直接 this.setState, 不然会溢出栈。需要对 prevProps 与 this.props 和 prevState 和 this.state 做一个判断再执行 this.setState。就类似while循环不能陷入死循环。

好吧。长篇大论了一番 react 的生命周期。不为什么,就因为它非常的重要。不管是对你的面试或者日常开发或者阅读理解别人的代码都是非常重要。哪个阶段会触发哪个生命周期,哪个能干什么哪个不能干什么,哪个更合适哪个不合适。来!干了它,咱们再继续往下看!

......
.....
....
...
..
.
感谢你能看到这里,咱们继续往下凿....
.
..
...
....
.....
......

接下来要研究的,就是重头戏--setState更新过程,可能你看过的文档都告诉你,setState不会保证立即执行,而是会在某个时机批量更新所有的component。

【17】props 和 state 的区别
  1. "props"是别人的, props实现组件间的状态传递,props从父组件到子组建的数据传递;"state"是自己的,state只能定义在组件内部,定义组件的自己的状态。
  2. props 是不可变的; state 可以通过this.setState改变

那么问题来了: 为什么要设定这个批量机制,这个批量更新的过程到底又是如何执行的呢?

【18】props vs state
? props state
可以从父组件获得初始值吗? Yes Yes
可以被父组件改变吗? Yes No
内部(当前)组件可以设置默认值吗? Yes Yes
可以改变内部(当前)组件吗? No Yes
可以为子组件设置初始值吗? Yes Yes
可以改变子组件吗? Yes No

本文由10bet发布于Web前端,转载请注明出处:10bet:关于 react 那些小知识点儿

关键词:

最火资讯