点击此处查看全文
Redux VS React Context:你应该选择哪一个?
最初发布在我的博客上
React context 已经存在一段时间了。随着 React hooks 的出现,它现在变得更好了。它有很多优点,包括 context API 不需要任何第三方库。我们可以在 React 应用中使用它来像 Redux 一样管理状态。
在本文中,我们将使用 React context 管理状态,亲自体验它在状态管理方面是否比 Redux 更好。顺便说一句,这篇文章是我上一篇的后续。
注意:本文仅涵盖 context API。我们将使用 React context 构建相同的项目。如果您对如何使用 redux 管理状态感兴趣,我之前的文章可能会对您有所帮助。
否则我们就开始吧。
先决条件
为了能够跟上本文,你至少需要了解 React 的基础知识和高级功能,尤其是 React Hooks。掌握 Redux 也会有所帮助。
设置项目
如果你准备好了,我们现在可以通过运行以下命令创建一个新的 React 应用程序:
npx create-react-app react-context-hooks-example
然后,我们必须创建一些文件。
containers
在中添加一个文件夹src
,然后创建Articles.js
文件。
import React, { useState } from "react"
import Article from "../components/Article/Article"
const Articles = () => {
const [articles, setArticles] = useState([
{ id: 1, title: "post 1", body: "Quisque cursus, metus vitae pharetra" },
{ id: 2, title: "post 2", body: "Quisque cursus, metus vitae pharetra" },
])
return (
<div>
{articles.map(article => (
<Article key={article.id} article={article} />
))}
</div>
)
}
export default Articles
components
在中添加一个文件夹src
,然后创建AddArticle/AddArticle.js
和Article/Article.js
。- 在
Article.js
import React from "react"
import "./Article.css"
const article = ({ article }) => (
<div className="article">
<h1>{article.title}</h1>
<p>{article.body}</p>
</div>
)
export default article
- 在
AddArticle.js
import React, { useState } from "react"
import "./AddArticle.css"
const AddArticle = () => {
const [article, setArticle] = useState()
const handleArticleData = e => {
setArticle({
...article,
[e.target.id]: e.target.value,
})
}
const addNewArticle = e => {
e.preventDefault()
// The logic will come later
}
return (
<form onSubmit={addNewArticle} className="add-article">
<input
type="text"
id="title"
placeholder="Title"
onChange={handleArticleData}
/>
<input
type="text"
id="body"
placeholder="Body"
onChange={handleArticleData}
/>
<button>Add article</button>
</form>
)
}
export default AddArticle
- 在
App.js
import React, { Fragment } from "react"
import Articles from "./containers/Articles"
import AddArticle from "./components/AddArticle/AddArticle";
function App() {
return (
<Fragment>
<AddArticle />
<Articles />
</Fragment>
)
}
export default App
因此,如果您已完成上述所有说明,我们就可以继续并开始实现上下文 API。
创建上下文
context
上下文帮助我们处理状态,而无需在每个组件上传递 props。只有需要的组件才会使用上下文。为了实现它,我们需要在项目中创建一个名为 的新文件夹(可选) ,并将以下代码添加到aricleContext.js
。
- 在
context/aricleContext.js
import React, { createContext, useState } from "react";
export const ArticleContext = createContext();
const ArticleProvider = ({ children }) => {
const [articles, setArticles] = useState([
{ id: 1, title: "post 1", body: "Quisque cursus, metus vitae pharetra" },
{ id: 2, title: "post 2", body: "Quisque cursus, metus vitae pharetra" }
]);
const saveArticle = article => {
const newArticle = {
id: Math.random(), // not really unique but it's just an example
title: article.title,
body: article.body
};
setArticles([...articles, newArticle ]);
};
return (
<ArticleContext.Provider value={{ articles, saveArticle }}>
{children}
</ArticleContext.Provider>
);
};
export default ArticleProvider;
React 库允许我们访问一种名为 的方法createContext
。正如您可能猜到的那样,我们可以使用它来创建上下文。在这里,我们没有向上下文 传递任何内容ArticleContext
,但您可以传递对象、数组、字符串等参数。然后我们定义一个函数来帮助我们通过 分发数据Provider
。我们赋予Provider
两个值:文章列表和添加文章的方法。顺便说一下,articles: articles
和与saveArticle:saveArticle
相同,这只是一种方便的语法,以防您感到困惑。 现在我们有了一个上下文,但是,我们需要提供上下文才能使用它。要做到这一点,我们需要用 和 包装我们的高级组件,这可能是最完美的选择。所以,让我们将它添加到。articles
saveArticle
ArticleProvider
App.js
App.js
提供背景信息
- 在
App.js
import React from "react";
import ArticleProvider from "./context/articleContext";
import Articles from "./containers/Articles";
import AddArticle from "./components/AddArticle/AddArticle";
function App() {
return (
<ArticleProvider>
<AddArticle />
<Articles />
</ArticleProvider>
);
}
export default App;
正如你所见,我们首先导入上下文提供程序ArticleProvider
,并包装需要使用上下文的组件。现在,如何使用上下文呢?我们该如何实现呢?你可能会惊讶地发现,使用钩子来使用上下文是多么简单。那就让我们开始吧。
使用上下文
我们将通过两个部分来使用上下文:Articles.js
和AddArticle.js
。
- 在
Articles.js
import React, { useContext } from "react";
import { ArticleContext } from "../context/articleContext";
import Article from "../components/Article/Article";
const Articles = () => {
const { articles } = useContext(ArticleContext);
return (
<div>
{articles.map(article => (
<Article key={article.id} article={article} />
))}
</div>
);
};
export default Articles;
有了 React Hooks,我们现在可以访问useContext
Hooks 了。正如你可能猜到的,它会帮助我们使用上下文。通过将上下文ArticleContext
作为参数传递给useContext
,它使我们能够访问保存在 中的状态articleContext.js
。在这里,我们只需要articles
。因此,我们将其提取出来,并映射到我们的文章中并显示它们。现在,让我们继续AddArticle.js
。
- 在
AddArticle.js
import React, { useState, useContext } from "react";
import "./AddArticle.css";
import { ArticleContext } from "../../context/articleContext";
const AddArticle = () => {
const { saveArticle } = useContext(ArticleContext);
const [article, setArticle] = useState();
const handleArticleData = e => {
setArticle({
...article,
[e.target.id]: e.target.value
});
};
const addNewArticle = e => {
e.preventDefault();
saveArticle(article);
};
return (
<form onSubmit={addNewArticle} className="add-article">
<input
type="text"
id="title"
placeholder="Title"
onChange={handleArticleData}
/>
<input
type="text"
id="body"
placeholder="Body"
onChange={handleArticleData}
/>
<button>Add article</button>
</form>
);
};
export default AddArticle;
与上一个示例一样,我们再次使用来从上下文中useContext
拉出。这样,我们现在可以安全地通过 React Context 添加新文章了。 现在,我们可以通过 React Context 管理整个应用程序的状态。但是,我们仍然可以通过另一个名为 的钩子来改进它。saveArticle
useReducer
使用 useReducer 增强上下文
hooksuseReducer
是 的替代方案useState
。它主要用于更复杂的状态。它useReducer
接受一个带有 React 应用初始状态的 reducer 函数,并返回当前状态,然后调度一个函数。
当我们开始实现它时,这一点会更加清晰。现在,我们必须reducer.js
在 context 文件夹中创建一个新文件,并在下面添加此代码块。
- 在
reducer.js
export const reducer = (state, action) => {
switch (action.type) {
case "ADD_ARTICLE":
return [
...state,
{
id: Math.random(), // not really unique but it's just an example
title: action.article.title,
body: action.article.body
}
];
default:
return state;
}
};
如您所见,该函数reducer
接收两个参数:state
和action
。然后,我们检查 action 的类型是否等于ADD_ARTICLE
(您可以创建一个常量或文件以避免输入错误)。如果是,则将一篇新文章添加到我们的状态中。如果您使用过 Redux,这种语法可能很熟悉。现在,添加新文章的逻辑由 Reducer 处理。我们还没做完,让我们将其添加到我们的上下文文件中。
import React, { createContext, useReducer } from "react";
import { reducer } from "./reducer";
export const ArticleContext = createContext();
const ArticleProvider = ({ children }) => {
const [articles, dispatch] = useReducer(reducer, [
{ id: 1, title: "post 1", body: "Quisque cursus, metus vitae pharetra" },
{ id: 2, title: "post 2", body: "Quisque cursus, metus vitae pharetra" }
]);
return (
<ArticleContext.Provider value={{ articles, dispatch }}>
{children}
</ArticleContext.Provider>
);
};
export default ArticleProvider;
在这里,我们首先导入useReducer
钩子和函数reducer
。正如我之前提到的,useReducer
接受一个函数。因此,我们必须将 reducer 函数传递给它,并将应用程序的初始状态作为第二个参数。现在,useReducer
让我们可以访问articles
和一个dispatch
函数(您可以随意命名)。现在,我们可以用 提供的新值来更新我们的提供程序useReducer
。
您已经可以看到,我们的上下文文件现在简洁多了。通过重命名向 添加了新文章的函数dispatch
,我们现在需要稍微更新一下我们的AddArticle.js
文件。
- 在
AddArticle.js
import React, { useState, useContext } from "react";
import "./AddArticle.css";
import { ArticleContext } from "../../context/articleContext";
const AddArticle = () => {
const { dispatch } = useContext(ArticleContext);
const [article, setArticle] = useState();
const handleArticleData = e => {
setArticle({
...article,
[e.target.id]: e.target.value
});
};
const addNewArticle = e => {
e.preventDefault();
dispatch({ type: "ADD_ARTICLE", article });
};
return (
<form onSubmit={addNewArticle} className="add-article">
<input
type="text"
id="title"
placeholder="Title"
onChange={handleArticleData}
/>
<input
type="text"
id="body"
placeholder="Body"
onChange={handleArticleData}
/>
<button>Add article</button>
</form>
);
};
export default AddArticle;
现在,我们不再需要拉取saveArticle
,而是获取dispatch
函数。它需要一种 action 类型ADD_ARTICLE
和一个值article
,该值将是新文章。这样,我们的项目现在可以通过 context API 和 React Hooks 进行管理了。
Redux VS React Context:谁赢了?
现在,您可以通过我们项目中的实现清楚地看到 Redux 和 React Context 之间的区别。然而,Redux 远未消亡或被 React Context 击败。Redux 是一个样板,需要一堆库。但它仍然是解决 props 钻取的绝佳解决方案。
带有钩子的 context API 更容易实现,并且不会增加包的大小。
然而,谁会赢呢?在我看来,对于语言环境、主题更改、用户身份验证等低频更新,React Context 完全没问题。但对于具有高频更新的更复杂状态,React Context 将不是一个好的解决方案。因为,React Context 将在每次更新时触发重新渲染,并且手动优化它可能非常困难。而像 Redux 这样的解决方案更容易实现。
您可以在这里找到完成的项目
文章来源:https://dev.to/ibrahima92/redux-vs-react-context-which-one-should-you-choose-2hhh