wuyuanlijie/blog

React在工作中的使用总结

Opened this issue · 2 comments

1.React组件的生命周期:

React 生命周期分为三种状态 1. 初始化 2.更新 3.销毁

图片

1. 初始化阶段

  • getDefaultPorps

设置默认的props,也可以用来设置组件的的默认属性,只调用一次

  • getInitialState

初始化组件state的值,其返回值会赋值给组件的this.state的属性,只调用一次

  • componentWillMount

该方法在完成渲染之前调用,在这里可以对组件的state最后的一次修改机会。

  • render

创建虚拟DOM,进行diff算法,更新dom树来在这里进行,此时就不能修改state

  • componentDidMount

render方法成功调用,并且真实的DOM都已经被渲染。在这里可以对数据进行请求

2. 更行的阶段

  • componentWillReceiveProps:

nextProps 代表从父组件获取到新的props——组件接受到新的props,会触发该函数,父组件的状态改变,给子组件传入新的prop值。用于组件的props变化后,更新state。

  • shouldComponentUpdate

react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候)

  • componentWillUpdate

组件初始化不调用,组件需要更新的时候调用,此时可以修改state

  • render
  • componentDidUpdate

组件初始化不调用,组件更新完成后调用,此时可以获取dom节点

3. 销毁的阶段

  • componentWillUnmount

组件将要卸载的时候调用,一定事件监听、定时器需要在这里清除。

2. webpack-dev-server与react-hot-loader两者的热更新替换有什么区别?

  • webpack-dev-server自己的--hot模式只能即时的刷新的页面,但状态保持不住,因为会重新的去刷新页面
  • react-hot-loader是在--hot的基础上做了额外的处理,来保证状态可以存下来,支持react组件状态不会丢失。根据stateNode节点的更新对比,只更新改变的reactDOM节点,从而保留了未改变的state的值。

react-hot-loader开启的流程:

  1. 启动webpack-dev-server命令:1、添加--inline。2、在webpack.config.js添加devServer: {inline: true}; 当配置中没有--inline选项的时候,我们需要在entry里面添加webpack-dev-server/client?http://localhost:port
  2. webpack-dev-server启动inline模式、且允许热更新hot,在plugins添加HotModuleReplacementPlugin()
  3. 在entry的入口entry的参数,添加react-hot-loader/patch
  4. 入口文件index.js 引入react-hot-loader的AppContainer组件,在这个组件下的所有的子组件都会发生变化,才会触发热更新。

3. React RouterV4 在react中的使用

React RouterV4遵循了React的理念:万物皆组件。
React RouterV4基于Learn管理多个Repository,其代码库包括:

  1. react-router React Router的核心
  2. react-router-dom 用于DOM绑定的React Router
  3. react-router-native 用于React Native的React Router
  4. react-router-redux React Router和Redux的集成
  5. react-router-config 静态路由的配置帮组助手

4. this.setState更新的问题

  • this.setState是异步的,所以在this.setState之后立即调用this.state是获取不到最新的数据的。

下面介绍三种方法来获取最新的数据:
1.回调函数

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

2.利用componentDidUpdate,因为在这个生命周期函数,说明页面已经渲染完成state、props都已经被渲染好了

componentDidUpdate(){
  console.log(this.state.val);
}

3.将this.setState放入到setTimeout函数中(因为在this.setState之后this.state是立即更新的!)

let self = this;
setTimeout(function(){
  self.setState({
    val: this.state.val + 1
  });
  console.log(self.state.val);
})

5. this.props.children容器类组件

  • 容器内组件,顾名思义就是里面的什么内容都不确定,只要负责装东西,它只是定义了外层结构形式,我们可以往里面塞任意的内容。
  • 在使用的自定义组件的时候,我们可以在里面嵌套jsx代码。嵌套的结构在组件的内部可以通过props.children获取。
  • 如果你在父组件的内部添加一个函数,在当前的组件中就可以通过this.props.children获取到这个函数(自己传入参数,但是函数是从父类传过来的)相当于vue的slot
  • this.props.chuldren是一个数组(它在当前的组件里面),是可以获取从父组件需要展示的列表项的内容!!!。
  • this.props 代表从父组件中获取到的内容

6. Props和State

  • React的核心**就是组件化的**,组件根据props和state两个参数,计算得到对应的界面的UI。可见,props和state是组件两个重要的数据来源。
  • props是组件对外的接口,state是组件对内的接口。组件内部可以引用其他的组件,组件之间的引用就形成了一个树状的结构(组件树)。组件的props数据,上层组件就可以通过下层组件的props属性进行传递。组件自身的也需要维护组件的数据,这就是对内的state。

如何设置一个组件state?

state必须能代表一个组件的UI呈现的完整状态集,组件对应UI的任何变化,都可以从state变化中反映出来,同时,state还必须是代表一个组件UI呈现的最小状态集。

  1. 变量是通过props从父组件获取的,则不是一个状态。
  2. 变量在组件的整个生命周期都保持不变的,则不是一个状态。
  3. 变量是可以通过state或props的已有数据获取得到的,则不是一个状态。
  4. 变量不在组件render中使用,则不是一个状态。这个变量更适合定义为组件的一个普通的属性。

如何正确的修改state

  1. 不能直接修改state

直接修改state,组件不会重新触发render

  1. state的更新是异步的

setState只是把修改的状态放入一个队列中,React会优化真正的时机,并且React会出于性能的问题,可能会多次将setState的状态修改合并成一次状态的修改。props的更新也是异步的。例如当我们连续点击两次按钮,连续调用两次this.setState,在React合并多次修改为一次的情况下,相当执行了以下的代码。后面的操作会覆盖前面的操作,最终的购买数量就只增加1个。

// 错误 这里只会触发一个添加的事件
Object.assign(
   previousState,
  {quantity: this.state.quantity + 1},
  {quantity: this.state.quantity + 1}
)
// 正确 接收一个函数作为参数的setState、第一个参数是组件修改组件前的state,后面是组件最新的props
this.setState((preState, props) => {
  counter: preState.quantity + 1,
})

3.state更新是一个浅合并(Shallow Merge)的过程

当调用setState修改组件的时候,只需要传入发生改变的状态变量,而不是组件完整的state,因为组件的的更新是一个浅合并的过程。只需要修改我们需要去修改的即可。

React建议我们把state当作一个不可变的对象,

  1. 状态的类型是不可变类型(数字、字符串、布尔值、null、undefined)
  2. 状态的类型是数组
    如有一个数组类型的状态books,当向books中增加一本书时,使用数组的concat方法或ES6的数组扩展语法(spread syntax)
// 方法一 concat
this.setState(preState => ({  // 返回一个对象 return {...}
  books: preState.books.concat(['React Book'])   // concat用于连接一个或多个数组。
}))
// 方法二 ES6 展开运算符
this.setState(preState => {
  books: [...preState.books, 'React Book']
}))

还可以使用slice来截取数组,使用filter来过滤数组,在setState里面参数添加一个函数,函数的参数是修改前的state。(concat、filter、slice返回一个新的数组,主要不要使用push、pop、shift、unshift、splice等方法修改数组,因为它们是在原数组的基础上修改的。)
3. 状态的类型是简单的对象
修改state中对象的方法的时候,以下的两种方式:

this.state = {
  owner = {
    name: '李杰',
    age: 20
  }
}
// 使用ES6的Object.assign方法
this.setState(preState => ({
    // Object.assign用于将所有可枚举属性的值从一个或多个源对象复制到目标对象
    owner: Object.assign({}, preState.owner, {name: 'lijie'})
  })
)
// 对象的展开运算符
this.setState(preState => ({
    owner: {...preState.owner, name: 'lijie'}
  })
)

=> React建议state不可变对象,所以当组件某个状态发生改变的时候,我们应该重新去创建一个新的状态,而不是修改原来的状态。避免重新去直接修改原对象的方法。

为什么React推荐组件的状态不可变对象?

  • 一是方便管理和调试
  • 二是出于性能的考虑,组件的状态是不可变的对象的时候,我们在组件的shouldComponentUpdate的方法中,仅需要比较状态的引用就可以判断状态是否改变,从而避免了不必要的render方法的调用。

学会去在react中使用箭头函数,箭头函数中的this指向为:当前调用对象的所在的上下文环境,也就是当前的组件。() => {}

问题:React.createClass会自动绑定每个方法的this到当前组件中,但是使用ES6 class或者纯函数的时候,我们需要手动去绑定this!

React中绑定this的几种方法:

  1. bind:直接在方法的内部添加bind
render () {
    return (
        <button onClick={ this.handleClick.bind(this, '李杰') }>点我、点我</div>
    )
}
  1. 构造函数内绑定
constructor(props) {
    super(props);
    this.handleClick = this.handle.bind(this);
}

// 这里我们也可以使用ES7的语法糖
constructor(props) {
    super(props);
    this.handleClick = ::this.handleClick;
}

  1. 箭头函数:会捕捉其所在的上下文this的值,作为自己的this的值。
render () {
    return (
        <button onClick={ () => this.handleClick() }>点我、点我</div>
    )
}

// 当然我们还可以这么写
handleClick = (e) => {
    console.log(this.state.message)
}
render () {
    return (
        <button onClick={ this.handleClick }>点我、点我</div>
    )
}