您现在的位置是:网站首页> 编程资料编程资料
react context优化四重奏教程示例_React_
2023-05-24
365人已围观
简介 react context优化四重奏教程示例_React_
一、前言
我们在使用react的过程中,经常会遇到需要跨层级传递数据的情况。props传递数据应用在这种场景下会极度繁琐,且不利于维护,于是context应运而生
官方解释: Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props
二、用法
在正文之前,先简单介绍一下context的三种消费方法:
- 1.通过
Consumer来消费上下文
const globalContext = React.createContext(); class TestUseContextSon1 extends React.Component { render() { return ( {(value) => { return {value.num}; }} ); } } export default class TestUseContext extends React.Component { constructor(props) { super(props); this.state = { num: 2, }; } render() { return ( ); } } - 2.通过静态变量
contextType来消费上下文
const globalContext = React.createContext(); class TestUseContextSon2 extends React.Component { static contextType = globalContext; render() { return {this.context.num}; } } export default class TestUseContext extends React.Component { ...省略... render() { return ( ); } } - 3.通过hooks
useContext来消费上下文
const globalContext = React.createContext(); const TestUseContextSon3 = (props) => { const con = useContext(globalContext); return {con.num}; }; export default class TestUseContext extends React.Component { ...省略... render() { return ( ); } } 比较:
Consumer既可以在类组件中使用,也可以在函数组件中使用contextType只能在类组件中使用useContext只能在函数组件中使用
三、缺点
这里有一个例子:
import React, { useState } from "react"; const globalContext = React.createContext(); const Son1 = () => { return Son1; }; const Son2 = () => { const value = useContext(globalContext); return Son2---{value.num}; }; export const Demo = () => { const [value, setValue] = useState({ num: 1 }); return ( ); }; 当我们改变value值时,会导致Son1、Son2都发生重渲染,但这与我们的初衷相悖,造成了额外的开销,我们期望做到的是Son1不执行,Son2重新渲染。在较长的一段时间内,我都认为是使用了context导致Provider下面的子组件发生了重渲染。网上也有很多解释没有说清楚,容易误导人。
实际情况是value的变化导致了Son1、Son2发生重渲染。如下示例: 即使我们不使用·context,当value发生变化时,Son1、Son2也会重渲染。
const Son1 = () => { return Son1; }; const Son2 = () => { return Son2; }; export const Demo = () => { const [value, setValue] = useState({ num: 1 }); return ( ); }; 那么问题来了,我们使用context的时候,必然要向Provider的value中传入一个状态,但是当状态改变时又不可避免的造成Provider下的所有子组件重新渲染,我们期望只有消费了上下文的子组件重新渲染,那么有什么方法能够避免这种额外的开销吗?
四、context优化
我们知道,所有消费了context的地方,只要Provider的value值发生变化,都会发生重渲染.只要我们有什么办法能够避开父组件状态发生变化,引起的子组件状态发生变化,那就可以减少很多不必要的开销。
一重奏--使用PureComponent
const globalContext = React.createContext(); class TestUseContextSon2 extends React.PureComponent { constructor(props) { super(props); this.state = {}; } render() { console.log("TestUseContextSon2----render"); return ( {(value) => { console.log("Consumer----handle"); return {value.num}; }} ); } } const TestUseContext = () => { const [value, setValue] = useState({ num: 1 }); return ( ); } 
初始化的时候,两个console各执行一遍

点击按钮之后,TestUseContextSon2----render没有打印,Consumer----handle打印,达到预期结果。
二重奏--使用shouldComponentUpdate
此处由于作者比较任性,省略100字,基本效果其实和PureComponent一致,不做过多描述。
三重奏--使用React.memo
React.memo既可以用于函数组件,也可以用于类组件
const globalContext = React.createContext(); const TestUseContextSon3 = React.memo(function (props) { console.log("TestUseContextSon3----render"); return ( {(value) => { console.log("Consumer----handle"); return {value.num}; }} ); }); const TestUseContext = () => { const [value, setValue] = useState({ num: 1 }); return ( ); } 
点击按钮之后,TestUseContextSon2----render没有打印,Consumer----handle打印,达到预期结果。 那如果我们使用useContext来消费上下文呢?
const TestUseContextSon4 = React.memo(function (props) { const con = useContext(globalContext); console.log("TestUseContextSon4----render"); return {con.num}; }); 
点击按钮之后,TestUseContextSon4----render打印,也就是说当我们使用useContext来消费上下文的时候,整个函数组件会重新执行。而Consumer仅仅只是局部执行,这意味更少的性能消耗。
四重奏--Provider再封装+props.children
上面所述的三种方法都存在一个弊端,Provider的直接下级组件都需要用memo、PureComponent、shouldComponentUpdate处理,才能屏蔽掉父级状态变化带来的影响,那么有没有一种更方便的方式呢?
代码如下:
/** 主题 */ const ThemeContext = React.createContext({ theme: "red" }); const ThemeProvider = (props) => { const [theme, setTheme] = useState({ theme: "red" }); console.log("ThemeProvider-----", theme.theme); return ( {props.children} ); }; const Son1 = function (props) { const { setTheme } = useContext(ThemeContext); return ; }; const Son2 = function (props) { const { theme } = useContext(ThemeContext); console.log("Son2----", theme.theme); return 主题----{theme.theme}; }; const Son4 = function (props) { console.log("Son4---没有使用上下文"); return 没有使用上下文; }; export default class ContextChildren extends React.Component { render() { return ( ); } } 在上面这段代码中,、、并没有直接放到ThemeContext.Provider组件下面,而是将该组件再次封装成ThemeProvider组件,并将状态管理也放在ThemeProvider组件中,然后通过props.children来引入组件子节点。
效果如下:

当我们点击按钮时,打印如下:

点击按钮,setTheme执行,状态由{ theme: "red" }变为{ theme: "blue" },引起ThemeProvider组件重新执行,打印ThemeProvider----- blue,组件Son2由于消费了上下文,重新执行,打印Son2---- blue
那么问题来了,为什么没有打印Son4呢?我们没有使用memo、PureComponent等处理Son4组件,但是它确实不会重新执行。
出现这种现象,其实是props.children引起的,props.children指向一个对象,这个对象中存放着、、执行的结果,ThemeProvider执行的时候,props.children指向的对象没有发生变化,只有当ContextChildren组件重新渲染的时候,、、才会重新执行,由于我们将状态放置于ThemeProvider组件中,所以ContextChildren组件不会重新渲染,、、也就不会重新执行,所以Son4---没有使用上下文没有打印。
那如果将ThemeProvider组件改成这样呢?
const ThemeProvider = (props) => { const [theme, setTheme] = useState({ theme: "red" }); console.log("ThemeProvider-----", theme.theme); const content = React.Children.map(props.children, (child) => { return child; }); return ( {content} ); }; Son4依然没有执行
提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
相关内容
- Vue $nextTick 为什么能获取到最新Dom源码解析_vue.js_
- JavaScript语法 JSON序列化之stringify实例详解_javascript技巧_
- JavaScript 转义字符JSON parse错误研究_javascript技巧_
- React Native可定制底板组件Magic Sheet使用示例_React_
- 8个开发者必须知道的JavaScript深层概念(推荐)_javascript技巧_
- Vue 结合Sortablejs实现table行排序功能_vue.js_
- VUE项目去除input 框值所有空格的操作方法_vue.js_
- Ant Design-vue 解决input前后空格问题(推荐)_vue.js_
- LayUI下拉树TreeSelect的使用解读_javascript技巧_
- LayUI—tree树形结构的使用解析_javascript技巧_
