React学习之1.数据传递Context

2024-08-23 01:48

本文主要是介绍React学习之1.数据传递Context,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

数据传递

React 的特性单向数据流,限制了正常情况下,数据只能自上而下传递(父组件 > 子组件)。
可以通过其他方式打破这种限制:

  1. 组件组合(props.children上绑定数据):省略了嵌套层级太多的组件层层传递数据。类似vue插槽(slot)
  2. 事件绑定:通过props将触发事件执行的函数绑定到子组件,实现子组件向上通讯。
  3. Context:创建context,使整个组件树共享数据。
  4. Redux:使用React的Context原理的工具。

静态页面配置

本次学习Context,创建一个简单的可以切换主题样式列表查询功能。

// index.css
*{margin:0;padding:0;box-sizing: border-box;}
html,body{font-size:14px;}#root{width:500px;margin: 50px auto;
}
.theme{margin-bottom:10px;
}
.theme label{margin-right:20px;
}
.dark .theme{color:#fff;
}
#searchBox{background-color: #eee;padding: 10px;
}
#searchBox.dark{background-color: #333;
}
.query{margin-bottom: 10px;
}
.query input[type=text]{height: 36px;line-height: 36px;font-size: 14px;padding:0 5px;border: 1px solid #999;
}
.query select{margin-left: 10px;height: 36px;line-height: 36px;padding:0 5px;border: 1px solid #999;
}.query.dark input[type=text],.query.dark select{border-color:#eee;background-color: #777;color:#ccc;
}.table{background-color: #fff;border: solid #999;border-width: 1px 1px 0 0;width: 100%;border-spacing: 0;
}
.table td,.table th{border: solid #999;border-width: 0 0 1px 1px;padding: 4px;
}
.table.dark,.table.dark td,.table.dark th{border-color:#eee;background-color: #777;color:#ccc;
}
// SearchApp.js
import React from 'react';// table行组件
class TableRow extends React.Component {render() {return <tr><td>category</td><td>name</td><td>price</td></tr>;}
}// table组件
class Table extends React.Component {render() {let rows = [];return (<table className={'table '}><thead><tr><th>Category</th><th>Name</th><th>Price</th></tr></thead><tbody>{rows}</tbody></table>);}
}// 查询项组件
class SearchBar extends React.Component {render() {return (<div className={'query '}><input type="text" /><select><option value="">All</option><option value="Sporting Goods">Sporting Goods</option><option value="Electronics">Electronics</option></select></div>);}
}// 查询功能组件
class SearchBox extends React.Component {render() {return (<div id="searchBox"><SearchBar /><Table /></div>);}
}// 功能容器组件
class SearchApp extends React.Component {render() {return (<SearchBox />);}
}export default SearchApp;

Context 简单使用

创建context对象

const ContextName = React.createContext(defaultValue);

每个Context对象都返回两个React组件:Provider和Consumer
Provider:允许消费组件(被包裹的组件树)订阅Context的变更。
Consumer:在子孙级组件使用,允许消费组件订阅Context变更。子元素是一个函数。用于一个组件消费多个Context 或 传递函数变更Context的默认值(实际上是变更Provider组件的value属性,不是变更Context对象的defaultValue)

读取context

指定组件的contextType属性(只能指向一个Context对象),就可以在组件内甚至render中使用this.context读取Context的值。

import React from 'react';// 创建一个context对象,存储主题相关数据,默认值light
const ThemeContext = React.createContext('light');
// 使用组件<ThemeContext.Provider> <ThemeContext.Consumer>// 或如此声明:const {Provider,Consumer} = ThemeContext;
// 使用组件<Provider> <Consumer>// table行组件
class TableRow extends React.Component {render() {return <tr><td>category</td><td>name</td><td>price</td></tr>;}
}// table组件
class Table extends React.Component {render() {const theme = this.context;let rows = [];return (<table className={'table ' + theme}><thead><tr><th>Category</th><th>Name</th><th>Price</th></tr></thead><tbody>{rows}</tbody></table>);}
}
// 指定组件的contextType
Table.contextType = ThemeContext;// 查询项组件
class SearchBar extends React.Component {// 指定contextType的新写法static contextType = ThemeContext;render() {const theme = this.context;return (<div className={'query ' + theme}><input type="text" /><select><option value="">All</option><option value="Sporting Goods">Sporting Goods</option><option value="Electronics">Electronics</option></select></div>);}
}// 查询功能组件
class SearchBox extends React.Component {render() {return (<div id="searchBox" className={this.context}><SearchBar /><Table /></div>);}
}
SearchBox.contextType = ThemeContext;// 功能容器组件
class SearchApp extends React.Component {render() {return (<ThemeContext.Provider value="dark"><div>SearchApp组件获取到的ThemeContext值是:{this.context}</div><SearchBox /></ThemeContext.Provider>);}
}
SearchApp.contextType = ThemeContext;export default SearchApp;

当 React 渲染一个订阅了 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
所以上面SearchApp组件读取的context值是 light,而不是它下级组件配置的默认值 dark

动态修改context

将context的默认值指向组件的state中的数据,并将一个可以变更这个数据的方法传递给context。可以使组件树中的组件动态的修改context。

Provider传递函数

import React from 'react';// 默认值将被组件的state替换,可以不设置
const ThemeContext = React.createContext();
// 或设置一个默认值,避免意外
// const ThemeContext = React.createContext({
//     theme: 'light',
//     toggleTheme: () => {}
// });// table行组件
class TableRow extends React.Component {render() {return <tr><td>category</td><td>name</td><td>price</td></tr>;}
}// table组件
class Table extends React.Component {render() {const {theme} = this.context;let rows = [];return (<table className={'table ' + theme}><thead><tr><th>Category</th><th>Name</th><th>Price</th></tr></thead><tbody>{rows}</tbody></table>);}
}
// 指定组件的contextType
Table.contextType = ThemeContext;// 查询项组件
class SearchBar extends React.Component {// 指定contextType的新写法static contextType = ThemeContext;render() {const {theme} = this.context;return (<div className={'query ' + theme}><input type="text" /><select><option value="">All</option><option value="Sporting Goods">Sporting Goods</option><option value="Electronics">Electronics</option></select></div>);}
}// 查询功能组件
class SearchBox extends React.Component {render() {const {theme,toggleTheme} = this.context;return (<div id="searchBox" className={theme}><div className="theme"><label><input type="radio" name="theme" value="light" checked={theme==='light'} onChange={(e) => {toggleTheme(e.target.value)}} /> light</label><label><input type="radio" name="theme" value="dark" checked={theme==='dark'} onChange={(e) => {toggleTheme(e.target.value)}} /> dark</label></div><SearchBar /><Table /></div>);}
}
SearchBox.contextType = ThemeContext;// 功能容器组件
class SearchApp extends React.Component {constructor(props) {super(props);this.toggleTheme = this.toggleTheme.bind(this);this.state = {theme: 'dark',toggleTheme: this.toggleTheme}}// 变更主题toggleTheme(val) {this.setState({theme: val})}render() {const {theme,toggleTheme} = this.state;return (// 或直接指向this.state<ThemeContext.Provider value={{theme,toggleTheme}}><SearchBox /></ThemeContext.Provider>);}
}
// SearchApp.contextType = ThemeContext; // 使用自己的state设置默认值,不需要读取Contextexport default SearchApp;

Consumer传递函数

在子孙级组件使用Consumer,可以通过函数传递context的值,而不需要指定组件的contextType。
修改SearchBox组件,使功能与上例一样:

// 查询功能组件
class SearchBox extends React.Component {render() {// const {theme,toggleTheme} = this.context; // 未指定contextType,this.context此时为{}return (<ThemeContext.Consumer>{({theme, toggleTheme}) => {return (<div id="searchBox" className={theme}><div className="theme"><label><input type="radio" name="theme" value="light" checked={theme==='light'} onChange={(e) => {toggleTheme(e.target.value)}} /> light</label><label><input type="radio" name="theme" value="dark" checked={theme==='dark'} onChange={(e) => {toggleTheme(e.target.value)}} /> dark</label></div><SearchBar /><Table /></div>);}}</ThemeContext.Consumer>);}
}
// SearchBox.contextType = ThemeContext; // 不用指定contextType

使用多个Context

嵌套Provider

创建一个Context,用于共享产品列表数据和筛选方法
通过嵌套Provider组件使用多个Context。

import React from 'react';const ThemeContext = React.createContext();
const ProductsContext = React.createContext();// table行组件
class TableRow extends React.Component {render() {return <tr><td>{this.props.category}</td><td>{this.props.name}</td><td>{this.props.price}</td></tr>;}
}// table组件
class Table extends React.Component {render() {// const {theme} = this.context;const theme = 'dark'; // contextType只能指定一个Context对象const {products} = this.context;let rows = products.map((v,i)=><TableRow {...v} key={i}></TableRow>);return (<table className={'table ' + theme}><thead><tr><th>Category</th><th>Name</th><th>Price</th></tr></thead><tbody>{rows}</tbody></table>);}
}
// Table.contextType = ThemeContext;
Table.contextType = ProductsContext; // 改为指向ProductsContext// 查询项组件
class SearchBar extends React.Component {// static contextType = ThemeContext;static contextType = ProductsContext; // 改为指向ProductsContextrender() {// const {theme} = this.context;const theme = 'dark'; // contextType只能指定一个Context对象const {searchList,keyword,category} = this.context;return (<div className={'query ' + theme}><input type="text" name="keyword" value={keyword} onChange={searchList} /><select name="category" value={category} onChange={searchList} ><option value="">All</option><option value="Sporting Goods">Sporting Goods</option><option value="Electronics">Electronics</option></select></div>);}
}// 查询功能组件
class SearchBox extends React.Component {render() {return (<ThemeContext.Consumer>{({theme, toggleTheme}) => (<div id="searchBox" className={theme}><div className="theme"><label><input type="radio" name="theme" value="light" checked={theme==='light'} onChange={(e) => {toggleTheme(e.target.value)}} /> light</label><label><input type="radio" name="theme" value="dark" checked={theme==='dark'} onChange={(e) => {toggleTheme(e.target.value)}} /> dark</label></div><SearchBar /><Table /></div>)}</ThemeContext.Consumer>);}
}// 功能容器组件
class SearchApp extends React.Component {constructor(props) {super(props);this.toggleTheme = this.toggleTheme.bind(this);this.searchList = this.searchList.bind(this);this.products = [{ category: "Sporting Goods", price: "$49.99", name: "Football" },{ category: "Sporting Goods", price: "$9.99", name: "Baseball" },{ category: "Sporting Goods", price: "$29.99", name: "Basketball" },{ category: "Electronics", price: "$99.99", name: "iPod Touch" },{ category: "Electronics", price: "$399.99", name: "iPhone 5" },{ category: "Electronics", price: "$199.99", name: "Nexus 7" }];this.state = {theme: 'dark',toggleTheme: this.toggleTheme,products: this.products,searchList: this.searchList,keyword: '',category: ''}}// 变更主题toggleTheme(val) {this.setState({theme: val})}// 筛选产品列表searchList(event) {let {keyword,category} = this.state;let products = this.products;let value = event.target.value;if (event.target.name === 'category') {category = value;}if (event.target.name === 'keyword') {keyword = value;}if (category) {products = products.filter(v => {return v.category === category;});}if (keyword) {products = products.filter(v => {return v.name.indexOf(keyword) !== -1;});}this.setState({products,[event.target.name]: value})}render() {const {theme,toggleTheme,products,searchList,keyword,category} = this.state;return (<ProductsContext.Provider value={{products,searchList,keyword,category}}><ThemeContext.Provider value={{theme,toggleTheme}}><SearchBox /></ThemeContext.Provider></ProductsContext.Provider>);}
}export default SearchApp;

嵌套Consumer

虽然实现了传递多个Context,但是使用contextType只能指向一个Context对象,所以这种方式不能读取所有的Context。
这时就是使用Consumer的时候了,同样也是嵌套,修改代码:

import React from 'react';const ThemeContext = React.createContext();
const ProductsContext = React.createContext();// table行组件
class TableRow extends React.Component {render() {return <tr><td>{this.props.category}</td><td>{this.props.name}</td><td>{this.props.price}</td></tr>;}
}// table组件
class Table extends React.Component {render() {return (<ThemeContext.Consumer>{({theme}) => (<ProductsContext.Consumer>{({products}) => (<table className={'table ' + theme}><thead><tr><th>Category</th><th>Name</th><th>Price</th></tr></thead><tbody>{products.map((v,i)=><TableRow {...v} key={i}></TableRow>)}</tbody></table>)}</ProductsContext.Consumer>)}</ThemeContext.Consumer>);}
}
// Table.contextType = ThemeContext;
Table.contextType = ProductsContext; // 改为指向ProductsContext// 查询项组件
class SearchBar extends React.Component {render() {return (<ThemeContext.Consumer>{({theme}) => (<ProductsContext.Consumer>{({searchList,keyword,category}) => (<div className={'query ' + theme}><input type="text" name="keyword" value={keyword} onChange={searchList} /><select name="category" value={category} onChange={searchList} ><option value="">All</option><option value="Sporting Goods">Sporting Goods</option><option value="Electronics">Electronics</option></select></div>)}</ProductsContext.Consumer>)}</ThemeContext.Consumer>);}
}// 查询功能组件
class SearchBox extends React.Component {render() {return (<ThemeContext.Consumer>{({theme, toggleTheme}) => (<div id="searchBox" className={theme}><div className="theme"><label><input type="radio" name="theme" value="light" checked={theme==='light'} onChange={(e) => {toggleTheme(e.target.value)}} /> light</label><label><input type="radio" name="theme" value="dark" checked={theme==='dark'} onChange={(e) => {toggleTheme(e.target.value)}} /> dark</label></div><SearchBar /><Table /></div>)}</ThemeContext.Consumer>);}
}// 功能容器组件
class SearchApp extends React.Component {constructor(props) {super(props);this.toggleTheme = this.toggleTheme.bind(this);this.searchList = this.searchList.bind(this);this.products = [{ category: "Sporting Goods", price: "$49.99", name: "Football" },{ category: "Sporting Goods", price: "$9.99", name: "Baseball" },{ category: "Sporting Goods", price: "$29.99", name: "Basketball" },{ category: "Electronics", price: "$99.99", name: "iPod Touch" },{ category: "Electronics", price: "$399.99", name: "iPhone 5" },{ category: "Electronics", price: "$199.99", name: "Nexus 7" }];this.state = {theme: 'dark',toggleTheme: this.toggleTheme,products: this.products,searchList: this.searchList,keyword: '',category: ''}}// 变更主题toggleTheme(val) {this.setState({theme: val})}// 筛选产品列表searchList(event) {let {keyword,category} = this.state;let products = this.products;let value = event.target.value;if (event.target.name === 'category') {category = value;}if (event.target.name === 'keyword') {keyword = value;}if (category) {products = products.filter(v => {return v.category === category;});}if (keyword) {products = products.filter(v => {return v.name.indexOf(keyword) !== -1;});}this.setState({products,[event.target.name]: value})}render() {const {theme,toggleTheme,products,searchList,keyword,category} = this.state;return (<ProductsContext.Provider value={{products,searchList,keyword,category}}><ThemeContext.Provider value={{theme,toggleTheme}}><SearchBox /></ThemeContext.Provider></ProductsContext.Provider>);}
}export default SearchApp;

contextType和Consumer的使用

  1. contextType只对一个Context有效,如果一个组件只使用一个context,请使用contextType,如果一个组件需要读取多个context,那就需要使用Consumer。
  2. contextType简化了Consumer的使用,直接赋值即可。
  3. contextType可以在组件任意地方读取context,Consumber只能在<Consumber>的子元素函数中使用。

Context应用场景

Context依赖了类似“全局”的变量,来共享那些对一个组件树而言是“全局”的数据。
会使组件的复用性变差,应谨慎使用。
主要应用场景在于很多不同层级的组件需要访问一些同样的数据。

但是Context会让组件变得不纯粹,因为依赖了全局变量。所以这决定了Context一般不会大规模的使用。所以一般在一个组件中使用一个Context就好。
例如当前的locale,theme,一些缓存数据,或一些全局的公共数据。

如果只是像避免方便的进行数据传递,React更推荐使用组件组合来解决。

Redux:依据context原理和Flux架构,在最顶层通过一个集中的Data Store管理公共数据和状态变更,类似vuex。

参考

Context | React文档

这篇关于React学习之1.数据传递Context的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1098009

相关文章

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左

SpringBoot中4种数据水平分片策略

《SpringBoot中4种数据水平分片策略》数据水平分片作为一种水平扩展策略,通过将数据分散到多个物理节点上,有效解决了存储容量和性能瓶颈问题,下面小编就来和大家分享4种数据分片策略吧... 目录一、前言二、哈希分片2.1 原理2.2 SpringBoot实现2.3 优缺点分析2.4 适用场景三、范围分片

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

Redis分片集群、数据读写规则问题小结

《Redis分片集群、数据读写规则问题小结》本文介绍了Redis分片集群的原理,通过数据分片和哈希槽机制解决单机内存限制与写瓶颈问题,实现分布式存储和高并发处理,但存在通信开销大、维护复杂及对事务支持... 目录一、分片集群解android决的问题二、分片集群图解 分片集群特征如何解决的上述问题?(与哨兵模

HTML中meta标签的常见使用案例(示例详解)

《HTML中meta标签的常见使用案例(示例详解)》HTMLmeta标签用于提供文档元数据,涵盖字符编码、SEO优化、社交媒体集成、移动设备适配、浏览器控制及安全隐私设置,优化页面显示与搜索引擎索引... 目录html中meta标签的常见使用案例一、基础功能二、搜索引擎优化(seo)三、社交媒体集成四、移动

HTML input 标签示例详解

《HTMLinput标签示例详解》input标签主要用于接收用户的输入,随type属性值的不同,变换其具体功能,本文通过实例图文并茂的形式给大家介绍HTMLinput标签,感兴趣的朋友一... 目录通用属性输入框单行文本输入框 text密码输入框 password数字输入框 number电子邮件输入编程框

HTML img标签和超链接标签详细介绍

《HTMLimg标签和超链接标签详细介绍》:本文主要介绍了HTML中img标签的使用,包括src属性(指定图片路径)、相对/绝对路径区别、alt替代文本、title提示、宽高控制及边框设置等,详细内容请阅读本文,希望能对你有所帮助... 目录img 标签src 属性alt 属性title 属性width/h