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

相关文章

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

MySQL大表数据的分区与分库分表的实现

《MySQL大表数据的分区与分库分表的实现》数据库的分区和分库分表是两种常用的技术方案,本文主要介绍了MySQL大表数据的分区与分库分表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. mysql大表数据的分区1.1 什么是分区?1.2 分区的类型1.3 分区的优点1.4 分

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

Redis 中的热点键和数据倾斜示例详解

《Redis中的热点键和数据倾斜示例详解》热点键是指在Redis中被频繁访问的特定键,这些键由于其高访问频率,可能导致Redis服务器的性能问题,尤其是在高并发场景下,本文给大家介绍Redis中的热... 目录Redis 中的热点键和数据倾斜热点键(Hot Key)定义特点应对策略示例数据倾斜(Data S

Vue中组件之间传值的六种方式(完整版)

《Vue中组件之间传值的六种方式(完整版)》组件是vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,针对不同的使用场景,如何选择行之有效的通信方式... 目录前言方法一、props/$emit1.父组件向子组件传值2.子组件向父组件传值(通过事件形式)方

css中的 vertical-align与line-height作用详解

《css中的vertical-align与line-height作用详解》:本文主要介绍了CSS中的`vertical-align`和`line-height`属性,包括它们的作用、适用元素、属性值、常见使用场景、常见问题及解决方案,详细内容请阅读本文,希望能对你有所帮助... 目录vertical-ali

Python实现将MySQL中所有表的数据都导出为CSV文件并压缩

《Python实现将MySQL中所有表的数据都导出为CSV文件并压缩》这篇文章主要为大家详细介绍了如何使用Python将MySQL数据库中所有表的数据都导出为CSV文件到一个目录,并压缩为zip文件到... python将mysql数据库中所有表的数据都导出为CSV文件到一个目录,并压缩为zip文件到另一个

SpringBoot整合jasypt实现重要数据加密

《SpringBoot整合jasypt实现重要数据加密》Jasypt是一个专注于简化Java加密操作的开源工具,:本文主要介绍详细介绍了如何使用jasypt实现重要数据加密,感兴趣的小伙伴可... 目录jasypt简介 jasypt的优点SpringBoot使用jasypt创建mapper接口配置文件加密

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解