目标
- 掌握类组件的定义和使用;
- 掌握 React 组件间通信的方式;
- 掌握类组件的生命周期。
内容
- 在 React 中组件分为两种,类组件和函数组件。这里先学习一下类组件。
类组件
- 组件:对具有一定独立功能的数据与方法的封装,对外暴露接口,有利于代码功能的复用,且不用担心冲突。
定义类组件
- 类组件必须继承 React.Component;
- 组件类必须有 render 方法。
state 和 setState
- React 的组件类似于一种状态机,UI 会随着组建的状态发生相应的变化。所以在 React 中,用户交互时,只需要进行状态的切换即可;
- 在 React 中,状态应该是不可变值,而修改状态的唯一方法则是调用 setState 方法:
setState(updater [, callback]);
- updater:更新数据的 function/Object;
- callback:更新成功后的回调 function;
- 批处理:React 通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能;
- 浅合并:Object.assign();
- 调用 setState 之后,会触发生命周期,重新渲染组件。
- 同步与异步问题:
- 默认情况下,调用 setState 为异步,并且在同一操作中,多次调用 setState,会合并处理,只更新一次;
- 在 React 可以监控的地方:React 事件、React 组件的生命周期函数以及其它的 React 方法中,呈现异步表现,并且会对 setState进行合并操作;
- 在异步方法中,或原生事件中,setState 呈现同步表现,不会对 setState 进行合并处理。
事件
- React 中的事件是一个合成事件;
- 事件的 this 问题处理:在 constructor 中进行 this bind;或者使用箭头函数;
- 阻止默认事件、阻止冒泡;
组件间通信
- 在 React.js 中,数据是从上到下流动(传递)的,也就是一个父组件可以把它的 state、props 通过 props 传递给它的子组件,但是子组件不能修改 props,React.js 是单向数据流。如果子组件需要修改父组件状态(数据),是通过回调函数方式来完成的。
- 父级向子级通信 - props
- 把数据添加到子组件的属性中,然后子组件从 props 属性中获取父级传递过来的数据;
- 子集向父级通信:
- 在父级中定义相关的数据操作方法(或其它回调),把该方法传递给子级,在子级中调用该方法给父级传递消息。
context api —— 跨组件通信
- 创建 context:createContext;
- 向下传递数据:Provider(通过 value 属性);
- 接收 context 数据:Consumer(内容为函数),或者通过 ContextType。
- context.js 代码:
import { createContext } from 'react'; const context = createContext(); const { Consumer, Provider } = context; export { Consumer, Provider }; export default context;
- 根节点(祖先节点)代码:
import { PureComponent } from 'react'; import App from './app'; import { Provider } from './context'; export default class Com02 extends PureComponent { state = { name: 'yjw', age: 18, } render() { return <> <Provider value={this.state}> <App /> </Provider> </> } }
- 中间层代码:
import { Component } from "react"; import Count from "./count"; import Num from "./num"; class App extends Component { render() { return ( <> <Num /> <Count /> </> ); } } export default App;
- 子节点1代码:
import { Component } from "react"; import context from "./context"; class Count extends Component { static contextType = context; // contextType 为固定写法 render() { return ( <> <h2>Count</h2> <p>name: {this.context.name}</p> <p>age: {this.context.age}</p> </> ); } } export default Count;
- 子节点2代码:
import { Component } from "react"; import { Consumer } from "./context"; class Num extends Component { render() { return ( <> <Consumer> {(context) => { return ( <> <h2>Num</h2> <p>nage: {context.name}</p> <p>age: {context.age}</p> </> ) }} </Consumer> </> ); } } export default Num;
注:使用 context 时,shouldComponentUpdate 会失效。
生命周期函数
- 所谓的生命周期就是指某个事物从开始到结束的各个阶段,当然在 React.js 中指的是组件从创建到销毁的过程,React.js 在这个过程中的不同阶段调用的函数,通过这些函数,我们可以更加精确的对组件进行控制,前面我们一直在使用的 render 函数其实就是组件生命周期渲染阶段执行的函数;
- 在 React 中,生命周期函数经历过多个版本演化:
- 挂在阶段:
- constructor;
- static getDerivedStateFromProps(props, state):此时需要 return 一个值会作为当前组件的 state,如果 render 函数中需要同时从 props 和 state 中取值,不妨在这个生命周期函数中将数据全部转化到 state 中;但是通过这种方法转化后,如果某个值是 props 中的,通过 this.setState 无法修改;
- render:生产虚拟 DOM;
- componentDidMount:挂载完成,已经将虚拟 DOM 生产为真实 DOM,并且添加到 DOM 树中;这里多用于处理副作用:数据请求和 DOM 操作 [
这里潜藏着优化点
]。
- 更新阶段:
- static getDerivedStateFromProps(props, state){ // 这里可以直接 return props};更新阶段获取到的 props、state 为 nextProps 和 nextState,也就是说组件更新之后的 props 和 state;
- shouldComponentUpdate(nextProps, nextState)[
这里潜藏着优化点
]; - render;
- getSnapshotBeforeUpdate(prevProps, prevState);在组件已经生成了新的虚拟 DOM,但是还没有更新真实 DOM 之前执行,用于获取更新的一些 DOM 信息,如 return document.querySelector('#box').innerHTML;单独使用会报警告,必须配合 componentDidUpdate 使用;
- componentDidUpdate(prevProps, prevState, prevDom):这里的 prevDom 为 getSnapshotBeforeUpdate 返回的值,也多用于处理更新阶段的副作用[
这里潜藏着优化点
]。
- 卸载阶段:
- componentWillUnmount,多用于移除监听事件。
1、记忆:挂载阶段有四个生命周期、更新阶段有五个生命周期、卸载阶段有一个生命周期,4-5-1;render 之前的生命周期函数参数都是 nextProps、nextState,render 之后的生命周期函数参数都是 prevProps、prevState。
2、如果一次刷新,render 多次,一般有两种情况:第一、上面的三个潜藏优化点里面判断不正确,第二、hooks 里面的参数没有控制好。
总结
- 类组件的生命周期函数中,最好只在 componentDidMount 和 componentDidUpdate 中调用 setState;
- 类组件中,如果需要进行数据请求,官方建议只能在 componentDidMount 和 componentDidUpdate 两个生命周期函数中进行数据请求;
- 在 componentDidUpdate 中进行数据请求或其它行为之后调用 setState 方法,一定要把条件限定好;
- 在 React 中使用列表渲染,一定记得加 key;key 在 React 中的取值问题:列表中唯一、更新前后同一个元素的 key 不能发生变化;key 的作用:是给元素添加了一个身份标识,用于视图更新前后,判断这是否是同一个节点;
- 事件:事件定义类似于 JS 中的行间事件;尽量将事件处理函数声明在实例的属性中;React 中事件是一个合成事件,所以要注意和 JS 的时间处理函数在使用时有一些区别:React 的事件处理函数中,默认 this 指向 undefined;阻止默认事件,不能使用 return false,要使用 event.preventDefault();阻止冒泡:event.stopPropagation();
- setState 同步异步问题(批更新batchUpdate):在批更新可以监控到的地方——异步,监控不到的地方——同步;批更新可以监控到的地方:1.React 事件、2.生命周期函数;监控不到的地方:1.DOM 事件、2.异步函数中。
- 在 React 中要注意一个问题,父组件更新一定会引起子组件更新。