安装

安装脚手架:yarn add global create-react-app
进入项目所在目录
使用脚手架快速搭建react项目:create-react-app

认识React

理念:
函数式、数据不可变

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';

React元素和React函数组件

App1 = React.createElement(‘div’,null,n) 👉App1是一个 React元素
App2 = () => React.createElement(‘div’,null,n) 👉App2是一个 React函数组件

React元素:
React.createElement的返回值element可以代表一个div元素,但不是真正的div(DOM对象),不能直接插入DOM中,所以我们一般称返回值element为虚拟DOM对象

React函数组件:() => React元素
() =>React.createElement返回值element也可以代表一个div,但这个函数可以多次执行,每次得到最新的虚拟div
React会对比每次的虚拟div,找出不同,局部更新视图,找不同的算法叫做DOM Diff算法,避免跨线程操作DOM,先在内部虚拟DOM对象进行统一操作,提高效率(可在页面调试通过闪烁辨别是否整体替换DOM对象还是局部替换)

类组件和函数组件

首先明确本质也是函数
关注:
外部数据props
内部数据state(数据的:初始化、读、写)
事件绑定(书写格式的选择)

最大区别:
类组件有this,函数组件没有this

外部数据props

props认识:
<B name="frank" onClick={this.onClick}>hi</B> B 组件接收到的 props 的值是
{name:"frank", onClick:this.onClick, children: 'hi'}
类组件:
this.props.xxx

  1. class App extends React.Component {
  2. constructor(props){ //可在此处传入外部数据
  3. super(props);
  4. this.state={ //内部数据初始化
  5. n:0;
  6. m:0
  7. z:{
  8. name:"npc";
  9. age:18
  10. }
  11. }
  12. };

函数组件:
props.xxx

父组件向子组件传递数据

天然地使用props

子组件向父组件传递数据

也可以使用props
通过在父组件传递props函数给子组件

步骤:

  1. 在父组件中创建函数,使用props绑定给子组件<Child addFun={this.addFun}/>
  2. 子组件从props解构出对应的函数名 const { addFun } = props
  3. 子组件将自身数据作为实参传入接收的函数中调用,此处是在抽离出的数据处理逻辑代码中调用的,通过this.props.addFun(xxx),jsx部分的代码中没有直接调用,也不推荐直接在jsx中直接调用函数,但有些场景下只能在jsx中拿到实参,如何做到不自执行又能拿到实参?可以在jsx中使用匿名箭头函数,在箭头函数的函数体中直接调用调用函数,方便直接原地传入实参

<button onClick={deleteContent(id)}>删除数据</button>

  1. 父组件在自身的函数中设置形参接收子组件传递过来的参数const addFun =(xxx)=>{...可在父组件中接收子组件传来的数据做处理}

内部数据state

类组件:
this.state:{xxx:yyy;…}初始值
this.state.xxx读
this.setState(()=>{…})写 //setState()是异步更新,其中优先放置函数,手动返回最新值

函数组件:
const [n,setN] = React.useState(初始值) 第一项读,第二项写

复杂的state:

当state对象中出现多个属性需要修改时:
不要依赖react的shallow merge自动合并,需要自己手动使用…操作符 合并

shallow merge:React 只会检查新 state 和旧 state 第一层的区别,并把新 state 缺少的数据从旧 state 里拷贝过来

举例:
类组件:
第一层属性会自动合并
第二层属性开始不会自动合并,需要手动使用…操作符拷贝原有state对象

  1. class App extends React.Component {
  2. constructor(){
  3. super();
  4. this.state={ //数据初始化
  5. n:0;
  6. m:0
  7. z:{
  8. name:"npc";
  9. age:18
  10. }
  11. }
  12. };
  13. add(){
  14. this.setState( {...this.state,n:this.state.n+=1} ) //数据的写(优先写函数)
  15. };
  16. }

函数组件:
使用setN回调不会自动合并,需要使用setSate回调手动合并

  1. const App = () => {
  2. // const [n, setN] = React.useState(0); //数据初始化
  3. // const [m,setM] = React.useState(1) //可以分开写出多个对象,灵活控制
  4. const [state,setState] = React.useState(0) //可以统一写进state对象,往后需要解构
  5. return (
  6. <div className="App">
  7. n:{n} //数据的读
  8. <button
  9. onClick={() => {
  10. //setN(n + 1); //数据的写
  11. //setM(m + 2)
  12. setState(...state,state.n + 1)
  13. }}
  14. >
  15. +1
  16. </button>
  17. </div>
  18. );
  19. }

修改state的最佳实践

在需要需要修改state的地方,最好统一先将原始state使用…扩展运算符拷贝一份下来

eg.因为通常情况下state的数据较多,可能state对象中包含一个选项,但是选项又属于object[],需要对每个object做操作是不利于state整体更新的,办法是先将原始state拷贝下来赋值给新声明的变量(一般与原先state的选项同名),再在setState回调中统一覆盖原先的state

  1. addTodo = (todo)=>{
  2. let todos = [...this.state.todos , todo] //一般同名
  3. this.setState({
  4. todos:todos
  5. })
  6. }

setState异步更新解决机制

setState回调是异步更新的,会在页面UI更新完成后再更新数据,并不会同步渲染到页面上,若出现多个setState回调则无法正常渲染
解决方法:
利用setState(fn,callback)第二个回调参数(只适合两个修改的情况)(不推荐)
使用多个setState回调传函数,不要传对象,具体修改多少个就写多少个setState回调

  1. class Son extends React.Component{
  2. //addN = () => this.setState( {n: this.state.n + 1} );改为以下:
  3. onClick = () =>{
  4. this.setState((state) => ({ n:state.n + 1}) ) //函数返回值若为对象则用()包裹
  5. this.setState((state) => ({ m:state.m + 1}) )
  6. }
  7. render(){
  8. return <button onClick={ this.addN }>n+1</button>
  9. }
  10. }

注意:函数返回值若为对象则用()包裹,如this.setState( ()=> ({…}) )

  1. //踩坑:当input框输入值回车或确认按钮后,input框重置状态失败
  2. //分析:setState是异步更新的,会在页面UI更新完成后再更新数据,此时页面UI没有触发重新渲染,故不会更新数据
  3. //解决:this.state.content数据已重置,只需在jsx模板中添加绑定value值,react自动会进行UI绑定更新
  4. handleSubmit = (e) =>{
  5. e.preventDefault()
  6. //重置状态
  7. this.setState({
  8. content:''
  9. })
  10. }
  11. render(){
  12. return (
  13. ...
  14. <input type = 'text' onChange={this.handleChange} value={this.state.content}>//绑定value
  15. )
  16. }

事件绑定

类组件:

  1. class Son extends React.Component{
  2. addN = () => this.setState( {n: this.state.n + 1} );
  3. render(){
  4. return <button onClick={ this.addN }>n+1</button>
  5. }
  6. }

思考箭头函数和普通函数区别
普通函数有原型,箭头函数没有原型(深拷贝用法处)
普通函数挂在原型上,而不是实例对象上
箭头函数挂在每次的实例对象上,而不是原型上

函数组件:
onClick={ () => {setN(n + 1)} }直接内部写箭头函数
onClick={ onClick } 或者写函数名,然后外部声明该函数:
const onClick = ()=> {
setN(n + 1)
}

+1案例:

类组件:
熟悉书写格式

  1. class App extends React.Component {
  2. constructor(){
  3. super();
  4. this.state={ //数据初始化
  5. n:0
  6. }
  7. };
  8. add(){
  9. this.setState({n:this.state.n+=1}) //数据的写(优先写函数)
  10. };
  11. render(){
  12. return (
  13. <div className="App">
  14. n:{this.state.n} //数据的读
  15. <button onClick={()=>{ this.add()} }>+1</button> //事件绑定
  16. </div>
  17. );
  18. }
  19. }

函数组件:

  1. const App = () => {
  2. const [n, setN] = React.useState(0); //数据初始化
  3. return (
  4. <div className="App">
  5. n:{n} //数据的读
  6. <button
  7. onClick={() => {
  8. setN(n + 1); //数据的写
  9. }}
  10. >
  11. +1
  12. </button>
  13. </div>
  14. );
  15. };

有状态组件和无状态组件

也称:容器组件和UI组件,本质就是类组件和函数组件,只是区分应用场景,作为“容器是有状态的”作为“UI只是被动的接收数据和处理展示数据”
容器组件:

  • 包含state状态
  • 拥有生命周期钩子
  • 不包含UI
  • 使用类创建组件

UI组件:

  • 不包含state状态
  • 从props接收数据
  • 只包含UI
  • 使用函数创建组件

案例:

  • 容器组件模板中存放两个子组件;
  • AddContact.js子容器组件通过props函数传递给App.js父容器组件的内容,也会通过父容器组件绑定给Contact.js子UI组件的props进行实时的动态传递,更新子UI组件展示的数据,此时的App.js父容器组件相当于共享的状态组件

image.png

常用的数组API

react理念提倡不修改原数据,则state数据中常用数组api应该返回新数组:
map遍历,结合if判断语句,常用于筛选遍历,返回新数组
filter过滤,返回值为true的数据项组成的数组,常用于删除操作,保留不是目标id的数据项,返回新数组

认识JSX

抽离数据逻辑处理内容

此处举例过滤筛选的数据处理操作
数据的逻辑处理可写入return()的jsx内容中,但逻辑容易混乱,一般将数据的逻辑处理部分单独抽离出来为一个函数复制给变量,再将数据处理的变量结果放置于jsx部分

本质

本质是React.createElement()
即使用babel会自动将return(

)转译为React.createElement(“div”,…)

注意

jsx中label标签不能使用for属性,改为htmlFor

表单数据提交

提交事件可以添加在form标签上,也可以添加在button标签上
添加在form标签上的事件使用onSubmit事件,添加在button上的事件为onClick事件

  • onSubmit事件默认为form表单提交,可以提交表单内容
  • onClick事件则是指定处理函数,也可以完成表单提交工作,但通常用作异步提交,可以走后台代码

虚拟DOM

内容是怎么在react当中进行渲染的?
并不是每次都完整地更新我们所渲染的整个内容
而是根据新状态state得出新的jsx模板内容,和当前状态state得到的jsx模板内容做对比,找到不同再更新到真实的DOM中,加快浏览器的运行效率

类组件生命周期

组件初始化阶段:

constructor() 初始化props、state,一般初始化state,因为props固定,其中不能调用setState回调,
可以用来bind this,但一般使用箭头函数并写在外部

shouldComponentUpdate() return false阻止更新,return true 同意更新(勿忘)
面试:shouldComponentUpdate()有什么用?
答案:它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新

  1. class CounterButton extends React.Component {
  2. onClick = () => {
  3. this.setState(state => ({ n:state.n +1})) //两个不同地址的对象,尽管渲染结果一样,
  4. this.setState(state => ({ n:state.n -1})) //react依然会render,
  5. //但是虚拟DOM在diff的时候发现UI没有变化,于是UI不变
  6. }
  7. shouldComponentUpdate(newProps,newState) {
  8. if(newState.n === this.state.n) {
  9. return false
  10. }else{
  11. return true
  12. }
  13. }
  14. }
  1. class CounterButton extends React.PureComponent {
  2. ...
  3. //只需要将React.Component 改为 React.PureComponent
  4. }
  5. //只是浅比较,适用于大部分时候

PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。

render() 创建虚拟DOM
展示视图
只能有一个根元素,多个则用 / <></>包裹
render里面可以写if else
可以写三元表达式
不能直接写for循环因为for循环第一次return就是结束了,除非声明一个变量,每次循环都将结果push进变量,最后return变量
循环一般用数组方法遍历如array.map(注意所有循环都要加key)

  1. render(){
  2. return this.state.array.map(n=><span key={n}>{n}</span>)
  3. }

componentDidMount() 组件已出现在页面
在元素插入页面后执行代码,这些代码依赖DOM
比如需要获取DOM高度、发起网络请求加载数据
首次DOM渲染会执行此钩子
补充:快速获取DOM:Refs的使用

  1. class MyComponent extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.myRef = React.createRef();
  5. }
  6. render() {
  7. return <div ref={this.myRef} />;
  8. }
  9. }
  10. //访问当前DOM
  11. const node = this.myRef.current;

组件更新阶段:

componentDidUpdate() 组件已更新
在视图更新后执行代码
也可以发起网络请求更新数据
首次渲染不会执行此钩子,第二次才开始渲染
在此处setState可能会引起死循环,除非放在if判断语句中,但不推荐此用法
若shouldComponentUpdate()返回false,则不触发此钩子

组件销毁阶段:

componentWillUnmount() 组件将要死
组件将要 被移出页面然后被销毁 的时候执行代码(移出页面是UI层次,销毁是内存层次)
常用于路由中触发,一般很少去手动销毁东西
unmount 过的组件 不会再次mount,因为内存数据已经销毁
做一些收尾的操作:比如:关闭定时器,取消订阅消息等
举例:
在 DidMount里面监听了window.scroll,那么就要在WillUnmount里面取消监听
在DidMount里面创建了Timer计数器,那么就要在WillUnmount里面取消Timer
在DidMount里面创建了网络请求,那么就要在WillUnmount里面取消请求
即 谁污染了谁终要被清理

函数组件声明周期

创建方式
箭头函数、普通函数

可以代替类组件
没有state?使用useState

没有生命周期?使用useEffect

初始化阶段

模拟constructor即函数组件 return 之前
模拟shouldComponentUpdate,使用React.memo和useMemo
模拟render,return就是render
模拟componentDidMount,useEffect( () => { console.log('第一次渲染')}, [] ) 传空数组默认首次渲染

更新阶段

模拟componentDidUpdate
useEffect( ()=>{ console.log('任意属性变更')} ) 不传数组则每次都渲染
useEffect( ()=>{console.log('n变了')}, [n,m,...]) [n,m,…]表示前面函数依赖、依据的变量发生改变才渲染
遇到问题:注意此处的用法在首次也进行了渲染,如何避免首次渲染?
解决方法:使用自定义的hook,完整版见下

  1. const [count, setCount] = useState(0) //重新声明一个变量
  2. useEffect(() => {
  3. setCount(count + 1) //每次n变化的时候UpdateCount+1
  4. }, [n]) //针对n变化
  5. useEffect(() => {
  6. if (count > 0) { //通过外置变量判断是否屏蔽首次渲染操作
  7. console.log('nnn')
  8. }
  9. }, [n]); //针对n变化

自定义 更新hook

自定义 屏蔽首次渲染 的hook,实现完整模拟componentDidUpdate

  1. //定义hook(可抽离到单独文件)
  2. const useUpdate = (fn, array) => {
  3. const [count, setCount] = useState(0)
  4. useEffect(() => {
  5. setCount(count+1)
  6. }, array)
  7. useEffect(() => {
  8. if (count > 0) {
  9. fn()
  10. }
  11. }, array);
  12. }
  13. //调用hook
  14. useUpdate(() => { console.log('变了') }, [n])

销毁阶段

模拟componentWillUnmount
useEffect( ()=>{ console.log('第一次渲染') return ()=>{console.log('组件将要死了') } })
蓝字部分,return一个箭头函数,函数将在组件即将销毁前执行

useState的原理
解决副作用问题

useState剖析

实现简易useState

理解setN不会直接作用于n:
App() 类比于
每次setN后App()会重新执行即render,useState(0)也会得到新的值,但是setN并不是直接改变n
setN可以理解为 一个中介,存放着对n数据的修改,即每个组件都有自己的数据x,将其命名为state,而useState会从x即state从读取n最新值

  1. //声明
  2. let _state //外部声明变量不会被myUseState重置
  3. const myUseState = (initialValue) => {
  4. _state = _state === undefined ? initialValue : _state //精确控制
  5. const setState = (newValue) => {
  6. _state = newValue
  7. render()
  8. }
  9. return [_state,setState]
  10. }
  11. const render =() => {
  12. ReactDOM.render(<App/>,rootElement)
  13. }
  14. //调用
  15. const App = () =>{
  16. const [n,setN] = myUseState(0)
  17. }

思考:为什么_state声明在外部,因为声明在函数内部则每次调用函数均会重置_state,没有缓存
避坑:_state = _state || initialValue错误,万一_state为0(falsy值),则走了initialValue

同一组件两个useState冲突

  1. let _state = [];
  2. let index = 0;
  3. const myUseState = (init) => {
  4. const currentIndex = index //-----------重点:外设变量的作用
  5. _state[currentIndex] = _state[currentIndex] === undefined ? init : _state[currentIndex]
  6. const setState = (newState) => {
  7. _state[currentIndex] = newState
  8. render()
  9. }
  10. index += 1; //--------------重点:在return 之前+1
  11. return [_state[currentIndex], setState]
  12. }
  13. const render = () => {
  14. index = 0 //--------------每次render需要重置index
  15. ReactDOM.render( < App / > , document.getElementById('root'))
  16. }
  17. //调用:
  18. const App = () => {
  19. console.log('App运行了')
  20. const [n, setN] = myUseState(0) //同一组件使用两个或多个useState
  21. const [m, setM] = myUseState(0)
  22. return ( <
  23. div > { n } <
  24. button onClick = {
  25. () => setN(n + 1)
  26. } > +1 < /button> {m} <
  27. button onClick = {
  28. () => setM(m + 1)
  29. } > +1 < /button> < /
  30. div >
  31. )
  32. }

解决冲突思路:state 设为数组类型,默认index初始值为0;return之前+1,以便下次进来定位到数组第二+个元素
重点:

  • 理解外设变量的意义,若没有外设currentIndex变量,只依赖index变量,那么return之前index+1则影响了数组第一位元素,index放return之后却没有意义,故外设不可修改值currentIndex作为中介
  • 每次render的时候需要重置index,否则保留了上次的缓存

缺点:

useState调用顺序
若第一次渲染时,n是第一个,m是第二个,z是第三个
则往后渲染时必须保持顺序完全一致,因为index值由 useState 出现的顺序决定的
以防万一,react不允许 if 等判断语句中使用useState
(vue3此处做了改进和优化)

总结

  • 每个函数组件对应一个React节点
  • 每个节点保存着_state和 index
  • useState会读取state[index]
  • index 由 useState 出现的顺序决定的
  • setState 会异步地修改state(此时不会及时渲染到页面中),并触发更新re-render

useRef 解决分身

出现问题:n的分身
出现场景:设置onClick+1和异步log(3秒后log值),先+1再log和 先log再+1出现的结果不同,因为onClick触发re-render出现新的App组件,新的App组件会有新的n,此时异步log仍是旧的n,因为log在旧的App组件上,针对的仍是旧的n(react理念,数据不可变,函数特性会创建新数据)
image.png
解决问题:希望有一个贯穿始终的state状态,即保持onClick和异步log的都是同一数据,避免因为频繁渲染组件产生新的组件新的state状态(useState固有缺陷:每次render都会产生新的state)

useRef补充:
useRef返回的是一个可变的ref对象,其 xxx.current 属性也就是 useRef(inital)中的inital初始化值。
所以注意 const xxxRef = useRef(0) 中, 此处的 xxxRef 实际是个对象为:{current:0}

思考:refs?

useRef另一用处:保持可变变量状态
如果需要一个值,在组件不断render时保持不变,返回的 ref对象 在组件的整个生命周期内保持不变,不会在render的时候重新生成新的组件新的ref对象

  1. const App = () => {
  2. const nRef = React.useRef(0)
  3. const update = React.useState(null)[1] //定义自动render函数 (小技巧)
  4. const log = () => { return setTimeout(() => { console.log(`n:${nRef.current}`) }, 1000) }
  5. return (
  6. <div >
  7. {nRef.current}
  8. <button onClick={
  9. () => {nRef.current += 1;
  10. update(nRef.current) } //难点:同时触发页面更新(即强制更新)
  11. }>+1</button>
  12. <button onClick={log}>log</button>
  13. </div>
  14. )
  15. }

为何要同时触发页面更新?因为不触发 update(即setN) 页面不会渲染更新(不推荐)
大部分情况useRef适用,而useContext较重

useContext 解决分身

这也是实现 保持可变变量状态 的另一种方式
全局变量 是全局的“上下文”
useContext 不仅能贯穿 始终,保持可变变量的状态,还能贯穿不同的组件

  1. //全局创建上下文Context:
  2. const xxxContext = React.createContext(initialValue)
  3. //外面包裹xxxContext.Provider, 通过value属性给作用域内组件传递state内部数据
  4. //包裹标签内相当是 xxxContext 的作用域
  5. <xxxContext.Provider value = { {yyy,zzz} }>
  6. <div>
  7. <ChildA /> //不管Child组件多深,value始终能传递给子组件
  8. <ChildB />
  9. </div>
  10. <xxxContext.Provider>
  11. //作用域内子组件 用useContext(xxxContext)使用上下文
  12. const ChildA = () => {
  13. const {yyy,zzz} = React.useContext(xxxContext)
  14. ...
  15. }

总结

针对useState,在每次任一数据变动后都会 re-render

  • 每次re-render,组件函数就会重新执行
  • 对应的组件所有state都会出现“分身”,没用的分身会被垃圾回收
  • 不希望出现分身则使用useRef / useContext等

React Context API

  • React Context API 是针对 状态管理 而设计的API,将计算的共享数据 保存在一个通用的顶层父组件上(provider component),和redux作用类似,无需手动每层组件添加props传输数据,独立于整个组件树之外创建一个共享的数据中心以供使用;
  • 以一种更直接有效的方式解决了早期使用 props来处理嵌套UI 的 状态共享问题

image.png

基本使用

创建Context上下文

  1. React.createContext

const MyContext = React.createContext(defaultValue);
只有当组件所处的组件树中没有匹配到 Provider 时,其 defaultValue 参数才会生效

  1. Context.Provider

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化

  1. //Provider 接收一个 value 属性,传递给消费组件
  2. class MyContextProvider extends Component {
  3. state ={...}
  4. render(){
  5. return (
  6. <MyContext.Provider value = { {yyy,zzz} }>
  7. <div>
  8. <ChildA /> //不管Child组件多深,value始终能传递给子组件
  9. <ChildB />
  10. </div>
  11. <MyContext.Provider>
  12. )
  13. }
  14. }
  1. 子组件(消费组件)订阅/访问 Provider React 组件(如此以来才能真正生效)

方法一:使用Class.contextType
可以让你使用 this.context 来获取最近 Context 上的值。你可以在任何生命周期中访问到它,包括 render 函数中

  1. class Navbar extends Component {
  2. static contextType = ThemeContext
  3. render(){
  4. let value = this.context;
  5. /* 基于这个值进行渲染工作 */
  6. }
  7. }

方法二:使用Context.Consumer
此方法和Context.Provider相对应

  1. <MyContext.Consumer>
  2. {(context) => {const {xxx,yyy} = context} /* 基于 context 值进行渲染*/}
  3. //此处为函数作为 子元素 进行渲染
  4. </MyContext.Consumer>

注意:Class.contextType 和 Context.Consumer 区别
Class.contextType 只能订阅单一context、用于类组件
Context.Consumer 可以订阅多个 context、用于类组件、函数组件

更新Context上下文

本质上是父子组件通信
子组件(消费组件)想要更新父组件的Context上下文数据,如何做?类比Vue,同样是通过调用父组件的对应方法来改变父组件Context,只是使用API具体不同

  1. //1.父组件内部定义函数
  2. //2.父组件通过value对象暴露方法给子组件
  3. class MyContextProvider extends Component {
  4. state ={...}
  5. + toggleTheme =()=>{...} //一般通过函数来修改状态,父组件定义函数
  6. render(){
  7. return (
  8. <MyContext.Provider value = { {yyy,zzz}, toggleTheme:this.toggleTheme}>//父组件暴露方法给子组件
  9. <div>
  10. <ChildA /> //不管Child组件多深,value始终能传递给子组件
  11. <ChildB />
  12. </div>
  13. <MyContext.Provider>
  14. )
  15. }
  16. }

可以将修改数据的逻辑抽离出来为一个单独的组件,如 ThemeToggle 组件

  1. //1.子组件 接收 父组件传过来的方法
  2. //2.子组件 调用 父组件传过来的方法
  3. class ThemeToggle extends Component {
  4. static contextType = ThemeContext
  5. render(){
  6. const {toggleTheme} = this.context; //从父组件中传过来的方法
  7. return (
  8. <button onClick={toggleTheme}>切换</button>
  9. )
  10. /* 基于这个值进行渲染工作 */
  11. }
  12. }

获取多个Context上下文

函数的返回值若 是基于Context渲染的 jsx模板,故使用 圆括号直接包括函数体

  1. //在提供初始 context 值的 App 组件中:可嵌套包裹多个provider
  2. function App() {
  3. return (
  4. <AuthContext.Provider value = {...}>
  5. <ThemeContext.Provider value = { {yyy,zzz} }>
  6. <div>
  7. <ChildA /> //不管Child组件多深,value始终能传递给子组件
  8. <ChildB />
  9. </div>
  10. </MyContext.Provider>
  11. </AuthContext.Provider>
  12. )
  13. }
  14. //子消费组件中:可嵌套包裹多个consumer
  15. function Nav() {
  16. return (
  17. <AuthContext.Consumer>
  18. {(authContext) => (
  19. <ThemeContext.Consumer>
  20. {(themeContext) => {
  21. const {isAuthenticated,toggleAuth} = authContext
  22. const {...} = themeContext
  23. return (...)/* 基于这个值进行渲染工作 */
  24. }}
  25. </UserContext.Consumer>
  26. )}
  27. </ThemeContext.Consumer>
  28. );
  29. }

image.png