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

相关文章

全面解析HTML5中Checkbox标签

《全面解析HTML5中Checkbox标签》Checkbox是HTML5中非常重要的表单元素之一,通过合理使用其属性和样式自定义方法,可以为用户提供丰富多样的交互体验,这篇文章给大家介绍HTML5中C... 在html5中,Checkbox(复选框)是一种常用的表单元素,允许用户在一组选项中选择多个项目。本

HTML5 搜索框Search Box详解

《HTML5搜索框SearchBox详解》HTML5的搜索框是一个强大的工具,能够有效提升用户体验,通过结合自动补全功能和适当的样式,可以创建出既美观又实用的搜索界面,这篇文章给大家介绍HTML5... html5 搜索框(Search Box)详解搜索框是一个用于输入查询内容的控件,通常用于网站或应用程

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

CSS3中的字体及相关属性详解

《CSS3中的字体及相关属性详解》:本文主要介绍了CSS3中的字体及相关属性,详细内容请阅读本文,希望能对你有所帮助... 字体网页字体的三个来源:用户机器上安装的字体,放心使用。保存在第三方网站上的字体,例如Typekit和Google,可以link标签链接到你的页面上。保存在你自己Web服务器上的字

使用SpringBoot整合Sharding Sphere实现数据脱敏的示例

《使用SpringBoot整合ShardingSphere实现数据脱敏的示例》ApacheShardingSphere数据脱敏模块,通过SQL拦截与改写实现敏感信息加密存储,解决手动处理繁琐及系统改... 目录痛点一:痛点二:脱敏配置Quick Start——Spring 显示配置:1.引入依赖2.创建脱敏

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

html 滚动条滚动过快会留下边框线的解决方案

《html滚动条滚动过快会留下边框线的解决方案》:本文主要介绍了html滚动条滚动过快会留下边框线的解决方案,解决方法很简单,详细内容请阅读本文,希望能对你有所帮助... 滚动条滚动过快时,会留下边框线但其实大部分时候是这样的,没有多出边框线的滚动条滚动过快时留下边框线的问题通常与滚动条样式和滚动行

详解如何使用Python构建从数据到文档的自动化工作流

《详解如何使用Python构建从数据到文档的自动化工作流》这篇文章将通过真实工作场景拆解,为大家展示如何用Python构建自动化工作流,让工具代替人力完成这些数字苦力活,感兴趣的小伙伴可以跟随小编一起... 目录一、Excel处理:从数据搬运工到智能分析师二、PDF处理:文档工厂的智能生产线三、邮件自动化:

Python数据分析与可视化的全面指南(从数据清洗到图表呈现)

《Python数据分析与可视化的全面指南(从数据清洗到图表呈现)》Python是数据分析与可视化领域中最受欢迎的编程语言之一,凭借其丰富的库和工具,Python能够帮助我们快速处理、分析数据并生成高质... 目录一、数据采集与初步探索二、数据清洗的七种武器1. 缺失值处理策略2. 异常值检测与修正3. 数据

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=