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将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

Spring 请求之传递 JSON 数据的操作方法

《Spring请求之传递JSON数据的操作方法》JSON就是一种数据格式,有自己的格式和语法,使用文本表示一个对象或数组的信息,因此JSON本质是字符串,主要负责在不同的语言中数据传递和交换,这... 目录jsON 概念JSON 语法JSON 的语法JSON 的两种结构JSON 字符串和 Java 对象互转

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

SpringBoot使用GZIP压缩反回数据问题

《SpringBoot使用GZIP压缩反回数据问题》:本文主要介绍SpringBoot使用GZIP压缩反回数据问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot使用GZIP压缩反回数据1、初识gzip2、gzip是什么,可以干什么?3、Spr