原文连接:React Context API: Managing State with Easecss
译者:OFEDhtml
使用最新的 React Context API 管理状态很是容易。如今就跟随我一块儿学习下它和 Redux 的区别以及它是如何使用的吧。前端
综述:React Context API 在 React 生态系统中并非个新鲜事物。不过,在 React 16.3.0
版本中作了一些改进。这些改进是如此巨大,以致于大大减小了咱们对 Redux 和其余高级状态管理库的需求。在本文中,你将经过一个实用教程了解到新的 React Context API 是如何取代 Redux 完成小型应用的状态管理的。node
在直奔主题以前,咱们先来快速回顾下 Redux,以便咱们更好的比较二者的区别。redux 是一个便于状态管理的 JavaScript 库。Redux 自己和 React 并无关系。来自世界各地的众多开发者选择在流行的前端框架(好比 React 和 Angular )中使用 Redux。react
说明一点,在本文中,状态管理指的是处理单页面应用(SPA)中产生的基于特定事件而触发的状态变化。好比,一个按钮的点击事件或者一条来自服务器的异步信息等,均可以触发应用状态的变化。git
在 Redux 中,你尤为须要注意下面几点:github
actions
,actions 描述了应该发生的事情。若是你对 Redux 并不熟悉而且你想要了解更多,请移步 Redux 的实用教程学习。npm
The React Context API 提供了一种经过组件树传递数据的方法,而没必要经过 props
属性一层层的传递。在 React 中,数据一般会做为一个属性从父组件传递到子组件。json
使用最新的 React Context API 须要三个关键步骤:redux
React.createContext
。这个方法会返回一个带有 Provider
和 Consumer
的对象。Provider
组件包裹在组件树的最外层,并接收一个 value 属性。value 属性能够是任何值。Consumer
组件,在组件树中 Provider
组件内部的任何地方都能获取到状态的子集。如你所见,所涉及的概念实际上与 Redux 没有什么不一样。事实上,甚至 Redux 也在其公共 API 的底层使用了 React Context API。然而,直到最近,Context API 才达到了足够成熟的水平。
如上所述,本文的目标是向你展现新的 Context API 如何在小型应用中替代 Redux。所以,你首先要用 Redux 建立一个简单的 React app,而后,你将学习如何删除这个状态管理库,以便更好地利用 React Context API。
你将构建的示例应用是一个处理一些流行食物及其来源的列表。这个应用还将包括一个搜索功能,使用户可以根据一些关键词过滤列表。
最终,你将建立一个相似下面所述的应用:
因为本文仅使用 React 和一些 NPM 库,所以除了 Node.js 和 NPM 以外,你什么都不须要。若是你尚未安装 Node.js 和 NPM,请前往官网下载并安装。
安装这些依赖后,你还须要安装 create-react-app
工具。这个工具帮助开发人员建立 React 项目。打开一个终端并运行如下命令来安装:
npm i -g create-react-app
复制代码
安装完 create-react-app
后,进入项目所在目录,执行如下命令:
create-react-app redux-vs-context
复制代码
几秒钟后,create-react-app
将完成应用程序的建立。在此以后,进入该工具建立的新目录,并安装 Redux:
# 进入应用目录
cd redux-vs-context
# 安装 Redux
npm i --save redux react-redux
复制代码
注意:
redux
是主库,react-redux
是促进 React 和 Redux 之间交互的库。简而言之,后者充当 React 和 Redux 之间的代理。
你已经搭建好了你的 React 应用,安装好了 Redux,如今,在你喜欢的开发工具中打开你的项目。而后在 src
文件夹中建立三个文件:
foods.json
:此文件包含一个用于保存食物及其来源信息的静态数组reducers.js
:此文件用于管理应用中 Redux 状态actions.js
:此文件用于保存应用中触发 Redux 状态改变的方法因此,首先,打开 foods.json
文件,添加以下内容:
[
{
"name": "Chinese Rice",
"origin": "China",
"continent": "Asia"
},
{
"name": "Amala",
"origin": "Nigeria",
"continent": "Africa"
},
{
"name": "Banku",
"origin": "Ghana",
"continent": "Africa"
},
{
"name": "Pão de Queijo",
"origin": "Brazil",
"continent": "South America"
},
{
"name": "Ewa Agoyin",
"origin": "Nigeria",
"continent": "Africa"
}
]
复制代码
如你所见,文件存储的数据并无什么特别。仅仅是一个包含着不一样国家不一样食物的数组。
在定义了 foods.json
文件后,你能够专一于建立你的 Redux store 了。回顾一下,store
是保存你的应用真实状态的惟一来源。打开你的 reducers.js
文件,添加如下代码:
import Food from './foods';
const initialState = {
food: Food,
searchTerm: '',
};
export default function reducer(state = initialState, action) {
// 根据 action type 区分
switch (action.type) {
case 'SEARCH_INPUT_CHANGED':
const {searchTerm} = action.payload;
return {
...state,
searchTerm: searchTerm,
food: searchTerm ? Food.filter(
(food) => (food.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1)
) : Food,
};
default:
return state;
}
}
复制代码
在上面的代码中,你能够看到 reducer
方法接收两个参数:state
和 action
。当你启动你的 React 应用,这个方法将得到它以前定义的 initialState
,当你 dispatch 一个 action 的实例时,这个方法将得到当前状态(再也不是 initialState
)。而后,基于这些 actions 的内容,reducer
方法将为你的应用生成一个新的状态。
接下来,你须要定义这些 actions 作什么。实际上,为了简单起见,你将定义一个单一的 action ,当用户在你的应用中输入搜索词时,这个 action 会被触发。所以,打开 actions.js
文件,并在其中插入如下代码:
function searchTermChanged(searchTerm) {
return {
type: 'SEARCH_INPUT_CHANGED',
payload: {searchTerm},
};
}
export default {
searchTermChanged,
};
复制代码
action
建立好以后,你须要作的下一件事就是将你的 App
组件包装到 react-redux
提供的 Provider
组件中。Provider 统一负责 React 应用的数据(即 store
)传递。
要使用 provider ,首先,你将使用 reducers.js
中定义的 initialState
建立 store
。而后,经过 Provider
组件,你将把 store
传给你的 App
。要完成这些任务,你必须打开 index.js
文件,并将其内容替换为:
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import reducers from './reducers';
import App from './App';
// 使用 reducers 信息建立 store。
// 这是由于 reducers 是 Redux Store 的控制中心。
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}> <App/> </Provider>,
document.getElementById('root')
);
复制代码
就是这样!你刚刚在 React 应用中配置完 Redux。如今,你必须实现 UI (用户界面),这样你的用户就可使用本节中实现的功能了。
如今,你已经完成了应用中的核心代码,你能够专一于构建你的用户界面。为此,打开你的 App.js
文件,用下方代码替换它的内容:
import React from 'react';
import {connect} from 'react-redux';
import actions from './actions';
import './App.css';
function App({food, searchTerm, searchTermChanged}) {
return (
<div> <div className="search"> <input type="text" name="search" placeholder="Search" value={searchTerm} onChange={e => searchTermChanged(e.target.value)} /> </div> <table> <thead> <tr> <th>Name</th> <th>Origin</th> <th>Continent</th> </tr> </thead> <tbody> {food.map(theFood => ( <tr key={theFood.name}> <td>{theFood.name}</td> <td>{theFood.origin}</td> <td>{theFood.continent}</td> </tr> ))} </tbody> </table> </div> ); } export default connect(store => store, actions)(App); 复制代码
对于未用过 Redux 的人来讲,他们惟一不熟悉的是用于封装 App
组件的 connect
方法。这个方法其实是一个高阶组件( HOC ),充当应用程序和 Redux 之间的粘合剂。
使用如下命令启动你的应用,你将可以在浏览器中访问你的应用:
npm run start
复制代码
然而,正如你所看到的,这个应用如今很难看。所以,为了让它看起来更好一点,你能够打开 App.css
文件,用如下内容替换它的内容:
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
line-height: 25px;
}
th {
background-color: #eee;
}
td, th {
text-align: center;
}
td:first-child {
text-align: left;
}
input {
min-width: 300px;
border: 1px solid #999;
border-radius: 2px;
line-height: 25px;
}
复制代码
完成了!你如今有了一个基本的 React 和 Redux 的应用,能够开始学习如何迁移到 Context API 上了。
在本节,你将要学习如何将你的 Redux 应用迁移到 React Context API 上。
幸运的是,你不须要在 Redux 和 Context API 之间作不少的重构。
首先,你必须从你的应用中移除 Redux 组件。为此,请打开终端,删除 redux
和 react-redux
库:
npm rm redux react-redux
复制代码
以后,删除应用中对这些库的引用代码。打开 App.js
删除如下几行:
import {connect} from 'react-redux';
import actions from './actions';
复制代码
而后,在相同的文件中,用下方的代码替换最后一行(以 export default 开头的那一行):
export default App;
复制代码
经过这些改变,你能够用 Context API 重写你的应用了。
要将以前的应用从 Redux 驱动的应用转换为使用 Context API,你须要一个 context 来存储应用的数据(该 context 将替换 Redux Store)。此外,你还须要一个 Context.Provider
组件,该组件包含 state
、props
和正常的 React 组件生命周期。
为此,你须要在 src
目录中建立一个 providers.js
文件,并向其中添加如下代码:
import React from 'react';
import Food from './foods';
const DEFAULT_STATE = { allFood: Food, searchTerm: '' };
export const ThemeContext = React.createContext(DEFAULT_STATE);
export default class Provider extends React.Component {
state = DEFAULT_STATE;
searchTermChanged = searchTerm => {
this.setState({searchTerm});
};
render() {
return (
<ThemeContext.Provider value={{ ...this.state, searchTermChanged: this.searchTermChanged, }}> {this.props.children} </ThemeContext.Provider>); } } 复制代码
上面代码中定义的 Provider
类负责将其余组件封装在 ThemeContext.Provider
中。经过这样作,你可让这些组件访问应用中的 state 和更改 state 的 searchTermChanged
方法。
若要在组件树中使用这些值,你须要建立一个 ThemeContext.Consumer
组件。这个组件将须要一个 render 渲染方法,该方法将接收上述 props
值做为参数。
所以,接下来,你须要在 src
目录中建立一个名为 consumer.js
的文件,并将如下代码写入其中:
import React from 'react';
import {ThemeContext} from './providers';
export default class Consumer extends React.Component {
render() {
const {children} = this.props;
return (
<ThemeContext.Consumer> {({allFood, searchTerm, searchTermChanged}) => { const food = searchTerm ? allFood.filter( food => food.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1 ) : allFood; return React.Children.map(children, child => React.cloneElement(child, { food, searchTerm, searchTermChanged, }) ); }} </ThemeContext.Consumer> ); } } 复制代码
如今,为了完成迁移,你将打开 index.js
文件,并在 render()
函数中,用 Consumer
组件包装 App
组件。此外,你须要将 Consumer
包装在 Provider
组件中。代码以下所示:
import React from 'react';
import ReactDOM from 'react-dom';
import Provider from './providers';
import Consumer from './consumer';
import App from './App';
ReactDOM.render(
<Provider> <Consumer> <App /> </Consumer> </Provider>,
document.getElementById('root')
);
复制代码
打完收工!你刚刚完成了从 Redux 到 React Context API 的迁移。若是你如今启动你的应用,你会发现整个应用运行如常。惟一不一样的是,你的应用再也不使用 Redux 了。
“新增的 React Context API 在减少应用体积方面是 Redux 的优良替代品。”
原文中有关于 Auth0 使用的详细教程,但译者认为此处内容和本文主题关系不大,故不做翻译。感兴趣者可移步原文阅读该部份内容。
Redux 是一个高级状态管理库,适合在构建大规模 React 应用时使用。另外一方面,Context API 能够用于字节大小级别数据更改的小规模 React 应用中。经过使用 Context API,你没必要像 reducers
、actions
等同样编写大量代码,就能经过状态变化完成逻辑展示。