react组件通信与生命周期
Posted ZZZ --- jh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react组件通信与生命周期相关的知识,希望对你有一定的参考价值。
react组件通信与生命周期
state和setState
state 组件自身状态
setState(updater[,callback])
- updater 更新数据的方法/对象
- callback 更新成功后的回调函数
- 异步: react通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能
- 浅合并: Object.assign()
React 在调用 setstate 后,react 会将传入的参数对象和组件当前的状态 合并,触发调和过程, 在调和过程中,react 会根据新的状态构建 react 元素树重新渲染整个 UI 界面,在得到元素树之后,react 会自动计算新老节点的差异,根据差异对界面进行最小化重新渲染
组件通信与数据流
在React.js中,数据是从上自下流动(传递)的,也就是一个父组件可以把它的state/props通过props传递给它的子组件,但是子组件不能修改props.
React.js是单向数据流,如果子组件需要修改父组件的状态(数据),是通过回调函数方式来完成的
-
父级向子级通信
把数据添加子组件的属性中,然后子组件中从props属性中,获取父级传递过来的数据
-
子级向父级通信
在父级中定义相关的数据操作方法(或其他回调函数),把该方法传递给子级,在子级中调用该方法父级传递消息
-
好友列表案例:
<!-- app.js --> import React, { Component } from 'react'; import FriendList from "./FriendList"; class App extends Component { render() { return (<div> <FriendList/> </div>) } } export default App;
<!-- FriendList.js --> import React, { Component } from 'react'; import './FriendList.css'; import data from './data'; import Dl from './dl' export default class FriendList extends Component { state = { isOpen : "" //哪一个是展开的 } // 传递给子组件, 点击哪个子组件,子组件就将自身的name传参过来 changeOpen = (name)=>{ this.setState({isOpen:name}) } render() { let {isOpen} = this.state; return ( <div className="friend-list"> { Object.keys(data).map((item, index) => { return ( <Dl key={index} name={item} value={data[item]} isOpen = {isOpen} changeOpen = {this.changeOpen} /> ) }) } </div> ) } }
<!-- dl.js --> import React,{ Component } from "react"; export default class Dl extends Component{ render(){ // console.log(this.props); let {title,list} = this.props.value; // name 自身数据的属性名 // isOpen 哪一项需要展开的 let {name,isOpen,changeOpen} = this.props; return ( <div className={"friend-group " + (name===isOpen ? 'expanded' : '')}> <dt onClick={()=>{ changeOpen(name) } }>{title} </dt> { list.map((item,index)=>{ return <dd key={index} >{item.name}</dd> }) } </div> ) } }
跨组件通信context-扩展
-
React.createContext(defaultValue);
{ Consumer, Provider } = createContext(defaultValue)
<!-- context.js --> import React,{ createContext } from "react"; let context = createContext(); let { Consumer, Provider } = context; export default context export { Consumer, Provider }
<!-- app.js --> import React, { Component } from 'react'; import FriendList from "./FriendList"; import { Provider } from "./context"; class App extends Component { // state = { // info:'别浪~' // } render() { return ( <Provider value={{ info:'猥琐发育' }}> <div> <FriendList /> </div> </Provider> ) } } export default App;
<!-- dl.js --> import React,{ Component } from "react"; import { Consumer } from "./context"; export default class Dl extends Component{ render(){ // console.log(this.props); let {title,list} = this.props.value; // name 自身数据的属性名 // isOpen 哪一项需要展开的 let {name,isOpen,changeOpen} = this.props; return ( <div className={"friend-group " + (name===isOpen ? 'expanded' : '')}> <dt onClick={()=>{ changeOpen(name) } }>{title} </dt> <p> <Consumer>{value=>value.info}</Consumer> </p> { list.map((item,index)=>{ return <dd key={index} >{item.name}</dd> }) } </div> ) } }
-
Context, Provoder在父组件调用Provider传递数据
value要传递数据
-
接收数据
-
class.contextType = Context;
-
static.contextType = Context;
this.context;
-
Context.Consumer {value=>value.info} 注意在使用不熟练时,最好不要再项目中使用context, context一般给第三方库使用
-
组件的生命周期
16.3版本之前的
挂载阶段
-
constructor
constructor() 中完成了react数据的初始化, 它接收两个参数:props和context, 当想在函数内部使用这两个参数时,需使用super()传入这两个参数.
注意: 只要使用了constructor()就必须写super(),否则会导致this指向错误.
-
componentWillMount — 组件渲染之前调用
一般用在服务器渲染时. 代表的过程是组件已经经历了constructor()初始化数据后, 但是还未渲染DOM时.
-
render
-
componentDidMount — 组件第一次渲染之后调用
组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求, 返回数据setState后组件会重新渲染.
更新阶段
父组件更新引起组件更新
-
componentWillReceiveProps(nextProps) — 在组件接收到一个新的props时调用
在接受父组件改变后的props需要重新渲染组件时用到的比较多
接收一个参数nextProps
通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件
-
shouldComponentUpdate(nextProps, nextState) — 判断组件是否更新html
主要用于性能优化(部分更新)
唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断
-
componentWillUpdate(nextProps, nextState) render — 组件即将更新html时调用
shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。
-
componentDidUpdate(prevProps, prevState) — 在组件完成更新后立即调用
组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。
组件自身更新
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
卸载阶段
-
componentWillUnmount — 在组件移除之前调用
在此处完成组件的卸载和数据的销毁
1 clear你在组件中所有的setTimeout和setInterval
2 移除所有组件中的监听 removeEventListener
3 有时候我们会碰到这个warning:
Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
原因:因为你在组件中的ajax请求返回setState,而你组件销毁的时候,请求还未完成,因此会报warning
解决方法:
componentDidMount() { this.isMount === true axios.post().then((res) => { this.isMount && this.setState({ // 增加条件ismount为true时 aaa:res }) }) } componentWillUnmount() { this.isMount === false }
16.3版本之后的
挂载阶段
- constructor
- static getDerivedStateFromProps(props, state)
- 注意this问题
- render
- componentDidMount
更新阶段
父组件更新引起组件更新
-
static getDerivedStateFromProps(props, state)
代替componentWillReceiveProps()。
老版本中的componentWillReceiveProps()方法判断前后两个 props 是否相同,如果不同再将新的 props 更新到相应的 state 上去。这样做一来会破坏 state 数据的单一数据源,导致组件状态变得不可预测,另一方面也会增加组件的重绘次数。这两者最大的不同就是:
在 componentWillReceiveProps 中,我们一般会做以下两件事,一是根据 props 来更新 state,二是触发一些回调,如动画或页面跳转等。- 在老版本的 React 中,这两件事我们都需要在 componentWillReceiveProps 中去做。
- 而在新版本中,官方将更新 state 与触发回调重新分配到了 getDerivedStateFromProps 与 componentDidUpdate 中,使得组件整体的更新逻辑更为清晰。而且在 getDerivedStateFromProps 中还禁止了组件去访问 this.props,强制让开发者去比较 nextProps 与 prevState 中的值,以确保当开发者用到 getDerivedStateFromProps 这个生命周期函数时,就是在根据当前的 props 来更新组件的 state,而不是去做其他一些让组件自身状态变得更加不可预测的事情。
-
shouldComponentUpdate()
-
componentWillUpdate()
-
render()
-
getSnapshotBeforeUpdate(prevProps, prevState)
代替componentWillUpdate。
常见的 componentWillUpdate 的用例是在组件更新前,读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。
这两者的区别在于:- 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。 - getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。
此生命周期返回的任何值都将作为参数传递给componentDidUpdate()
- 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
-
componentDidUpdate()
组件自身更新
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
卸载阶段
- componentWillUnmount
错误处理
解释: 当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用以下的方法
- static getDerivedStateFromError()
- componentDidCatch(error, info) 参考:http://projects.wojtekmaj.pl/react-lifecycle-methodsdiagram/
受控组件
- 需要同步value值(defaultValue, defaultChecked)
- 类似于vue中的
双向数据绑定
, 数据和视图之间可以相互影响
非受控组件
- 不需要同步value值(defaultValue, defaultChecked)
- 类似于
单向数据流
, 只可以数据改变视图
总结
组件通信的三种方式和生命周期都是react中的重要知识点,详细见解也可以去官网查看
以上是关于react组件通信与生命周期的主要内容,如果未能解决你的问题,请参考以下文章