Redux VS React Context:你应该选择哪一个?

2025-05-24

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
Enter fullscreen mode Exit fullscreen mode

然后,我们必须创建一些文件。

  • 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
Enter fullscreen mode Exit fullscreen mode
  • components在中添加一个文件夹src,然后创建AddArticle/AddArticle.jsArticle/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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

因此,如果您已完成上述所有说明,我们就可以继续并开始实现上下文 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;
Enter fullscreen mode Exit fullscreen mode

React 库允许我们访问一种名为 的方法createContext。正如您可能猜到的那样,我们可以使用它来创建上下文。在这里,我们没有向上下文 传递任何内容ArticleContext,但您可以传递对象、数组、字符串等参数。然后我们定义一个函数来帮助我们通过 分发数据Provider。我们赋予Provider两个值:文章列表和添加文章的方法。顺便说一下,articles: articlessaveArticle:saveArticle相同,这只是一种方便的语法,以防您感到困惑。 现在我们有了一个上下文,但是,我们需要提供上下文才能使用它。要做到这一点,我们需要用 和 包装我们的高级组件这可能是最完美的选择。所以,让我们将它添加到articlessaveArticle


ArticleProviderApp.jsApp.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;

Enter fullscreen mode Exit fullscreen mode

正如你所见,我们首先导入上下文提供程序ArticleProvider,并包装需要使用上下文的组件。现在,如何使用上下文呢?我们该如何实现呢?你可能会惊讶地发现,使用钩子来使用上下文是多么简单。那就让我们开始吧。

使用上下文

我们将通过两个部分来使用上下文:Articles.jsAddArticle.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;
Enter fullscreen mode Exit fullscreen mode

有了 React Hooks,我们现在可以访问useContextHooks 了。正如你可能猜到的,它会帮助我们使用上下文。通过将上下文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;
Enter fullscreen mode Exit fullscreen mode

与上一个示例一样,我们再次使用来从上下文中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;
  }
};

Enter fullscreen mode Exit fullscreen mode

如您所见,该函数reducer接收两个参数:stateaction。然后,我们检查 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;
Enter fullscreen mode Exit fullscreen mode

在这里,我们首先导入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;
Enter fullscreen mode Exit fullscreen mode

现在,我们不再需要拉取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 这样的解决方案更容易实现。

您可以在这里找到完成的项目

GitHub 徽标 ibrahima92 / react-context-hooks-example

通过 React 上下文管理的简单项目

文章来源:https://dev.to/ibrahima92/redux-vs-react-context-which-one-should-you-choose-2hhh
PREV
2020 年 10 个实用的原生 JavaScript 插件
NEXT
React TypeScript - 如何在 Hooks 上设置类型(+ 备忘单)