Sunshine168 / resume

My resume
3 stars 1 forks source link

将近大半年的一次react自测 #9

Open Sunshine168 opened 6 years ago

Sunshine168 commented 6 years ago

每日,每一段时间都在问自己自己干了什么,看一下自己知道什么,希望在不断进步的时候,不断更新。

PS:概念自问自答只是精炼我自己的看法,有不对请大家不吝指出。

react基础

生命周期

写一下react的组件的生命周期吧

加载期

constructor()
componentWillMount()
render()
componentDidMount()

更新期

componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componeneDidUpdate()

销毁期

componentWillUnmout()

错误捕捉

componentDidCatch()

不要调用setState的生命周期

shouldComponentUpdate()
componentWillUpdate()  // 这个周期是不能调用 会造成死循环
componentWillUnmount()

将生命周期函数声明为async会对组件有影响吗?

没有,函数内部可以使用 async/await 的语法糖。

为什么可以将生命周期函数声明为async function

因为即使是声明为 async 在调用的时候也是当成普通函数调用。


综合应用一下

main.js

import React from 'react';
import { render } from 'react-dom';
import Parent from './parent';

const styles = {
  fontFamily: 'sans-serif',
  textAlign: 'center',
};

class App extends React.Component {
   state =  {
     mount:true
   }
   render(){
     return (
       <div style={styles}>
        {this.state.mount?<Parent />:null}
         <h2>Start editing to see some magic happen {'\u2728'}</h2>
         <button 
         onClick={()=>{
           this.setState({
             mount:!this.state.mount
           })
         }}>test</button>
       </div>
     )
   }
}

render(<App />, document.getElementById('root'));

parent.js

import React from 'react';
import ChildComponent from './child.js'

export default class Parent extends React.Component {
  constructor(props) {
    super(props)
    console.log('parent:constructor')
  }

  componentWillMount() {
    console.log('parent: will mount')
  }

  componentDidMount() {
    console.log('parent: did mount')
  }

  componentDidUpdate() {
    console.log('parent: did update')
  }

  componentWillUnmount() {
    console.log('parent: will mount')
  }

  render() {
    console.log('parent render')
    return (
      <div>
        <ChildComponent refresh={()=>{
          console.log('did update A')
          this.setState({},()=>{
            console.log('did update B')
          })
        }}/> 
      </div>
    )
  }
}

child.js

import React from 'react';

export default class extends React.Component{
  constructor(props){
    super(props)
    console.log('child:constructor')
  }

componentWillMount(){
  console.log('child: will mount')
}

componentDidMount(){
  console.log('child: did mount')
}

componentDidUpdate(){
  console.log('child: did update')
}

componentWillUnmount(){
  console.log('child: will mount')
} 

  render(){
    console.log('child render')
   return (
     <div>
       <span>child Component </span>
       <button onClick={() => {
         this.props.refresh()
       }}>refresh</button>
     </div>
   )
  }
}

题一、请问在 App 第一次render的时候,控制台输出的语句是什么?

parent:constructor
parent: will mount
parent render
child:constructor
child: will mount
child render
child: did mount
parent: did mount

题二、请问在点击子组件refresh按钮按下的时候控制台输出的语句是什么?

did update A
parent render
child render
child: did update
parent: did update
did update B

题三、请问在第一次点击test按钮的时候控制台会输出什么?

parent: will mount
child: will mount

引出,为什么是这样的顺序?

解析virtual Dom的时候是递归的形式

组件更新是自顶向下。

Edit nknjpy162m

TODO // 实现一下一个简单的virtual Dom 解析

setState

setState 是异步还是同步,下面的输出结果是什么,为什么?

异步。

0,0,2,3。

生命周期内合并执行,合并执行就是将其并在一个事务中进行,进行批量更新。

Edit l7y0nq1o19

import React from "react";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    this.setState({
      count: this.state.count + 1
    });
    console.log(this.state.count);
    this.setState({
      count: this.state.count + 1
    });
    console.log(this.state.count);

    setTimeout(() => {
      this.setState({
        count: this.state.count + 1
      });
      console.log(this.state.count);
      this.setState({
        count: this.state.count + 1
      });
      console.log(this.state.count);
    });
  }

  render() {
    return <div>{this.state.count}</div>;
  }
}

setState的第二个参数有什么用?

延伸,为什么setState需要是异步的,举个例子?

class Parent extends React.Component{
    state = {
        a:1
    }
    render(){
        <Child a={this.state.a} increase={()=>{this.setState({
            a:this.state.a +1
        })}}/>
    }
}

在同步的情况下 setState 以后父组件的a是2,但是在更新组件前props到子组件的a却还是1 这样是不合理的

具体见 why setState is async

组件

PureComponent 和 Stateless Componet的区别:

前者可以具有自身的state,有组件的生命周期,后者不具备。 PureCompoent 在shouldComponentUpdate 上面进行简单的浅比较,不适合用于处理复杂状态。 Stateless Component本身没有更新机制,只能通过父组件更新。

状态管理

redux

redux的三大组成

action,store,reducer


redux的三大原则,讲讲了解。

单一数据源,数据只能通过action修改,reducer必须是纯函数。

单一数据源指的是,全部的store应该挂载在一颗store上。

总结起来就是单向数据流

关于redux中的异步数据流

redux本身是不支持异步数据流的,即在没有中间件的时候dispatch的必须是一个Object(目前的理解是原型链的继承自Object)

你了解处理异步action的中间件

redux-thunk

短小精悍的中间件

const INCREMENT_MONEY = 'INCREMENT_MONEY'

const mapDispatchToProps = (dispatch)=>({
  getMoney:()=>{
      setTimeout(()=>{
          dispatch({
              type:INCREMENT,
          })
      },500)
  }
})
// ...

redux-saga

适合处理比较复杂的异步问题,一些简单场景也提供便利的功能,saga中运行的任务是可取消的,像触发同一个任务的时候可以通过saga的机制总是执行最新的任务。

...

为什么reducers需要纯函数?

因为在遍历store中的key的时候,判断前后状态是通过浅比较。

mobx

通过observable包裹变量,通过Object.defineProperty重写get/set,通过atom发送信号到mobx中去。

更新机制,通过observer进行绑定组件。在绑定中通过reaction在render中收集依赖,再mobx收到依赖发送的信号的时候向绑定的组件发送更新信号,observer作为高阶组件再调用容器组件的forceUpdate 达到更新组件的目的。

实质,数据双向绑定。

拓展阅读

import React from "react";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    this.refBtn.addEventListener("click", this.onClick);
  }

  componentWillUnmount() {
    this.refBtn.removeEventListener("click", this.onClick);
  }

  onClick = () => {
    console.log("before setState", this.state.count);
    this.setState(state => ({ count: state.count + 1 }));
    console.log("after setState", this.state.count);
  };

  render() {
    return (
      <div>
        <button onClick={this.onClick}>React Event</button>
        <button ref={ref => (this.refBtn = ref)}>Direct DOM event</button>
      </div>
    );
  }
}

Why do we need middleware for async flow in Redux?

. So it is just easier from the maintenance point of view to extract action creators into separate functions.


Detail

https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux

https://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559#35415559

Sunshine168 commented 6 years ago

更正两个地方。 第一个地方是,生命周期执行的顺序,在constructor的时候 确实是跟virtual dom的解析有关系, 更深入的讲,后面相关生命周期的执行顺序是由于render的递归。 这点迟点会结合源码深入看一下。

第二个地方是关于setState 因为props的传递是异步的(组件更新才会更新Props) setState的异步性正是保证了这种安全。 这里依然不是很明确,需要进一步思考。