React Redux 和 Context Api - 如何通过上下文传递道具并保留它 "connected"?

React Redux & ContextApi - How to pass a prop via context and keeping it "connected"?

考虑在使用上下文 API 的 react-redux 应用程序上设置以下项目以避免 prop drilling。给出的示例已简化。

项目设置

  1. React 项目使用 React Redux
  2. 在某些情况下使用上下文 API 来避免道具钻探。
  3. Redux 商店有一个道具 posts,其中包含 posts
  4. 的列表
  5. 一个动作创建者deletePost(),它通过post id删除了某个post。
  6. 为了避免 prop drilling,postsdeletePosts() 都被添加到上下文 AppContext 中并由钩子函数 useApp().
  7. 返回
  8. posts 数组通过上下文传递,因此 connect() 函数不使用它。 重要

问题:

当动作被调度时,存储被更新但是组件没有被重新渲染(因为道具没有连接?)。当然,如果我通过 connect 函数传递道具并将其深入到子渲染工作正常。

解决方法是什么?

示例项目

示例项目可以在 codesandbox 中找到。打开控制台并尝试单击删除按钮。您将在 UI 中看到没有变化,而您可以在控制台中看到状态已更新。

代码

App.js

import Home from "./routes/Home";
import "./styles.css";
import { AppProvider } from "./context";

export default function App() {
  return (
    <AppProvider>
      <div className="App">
        <Home />
      </div>
    </AppProvider>
  );
}

context.js

import { useDispatch, useStore } from "react-redux";
import { useContext, createContext } from "react";
import { deletePost } from "./redux/actions/posts";

export const AppContext = createContext();

export const useApp = () => {
  return useContext(AppContext);
};

export const AppProvider = ({ children }) => {
  const dispatch = useDispatch();
  const {
    posts: { items: posts }
  } = useStore().getState();

  const value = {
    // props
    posts,

    // actions
    deletePost,
    dispatch
  };
  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};

Home.js

import { connect } from "react-redux";

import Post from "../components/Post";
import { useApp } from "../context";

const Home = () => {
  const { posts } = useApp();
  return (
    <section>
      {posts.map((p) => (
        <Post key={p.id} {...p} />
      ))}
    </section>
  );
};
/*
const mapProps = ({ posts: { items: posts } }) => {
  return {
    posts
  };
};
*/
export default connect()(Home);

Post.js

import { useApp } from "../context";

const Post = ({ title, content, id }) => {
  const { deletePost, dispatch } = useApp();
  
  const onDeleteClick = () => {
    console.log("delete it", id);
    dispatch(deletePost(id));
  };

  return (
    <article>
      <h1>{title}</h1>
      <p>{content}</p>
      <div className="toolbar">
        <button onClick={onDeleteClick}>Delete</button>
      </div>
    </article>
  );
};

export default Post;

您没有正确使用连接高阶组件方法。尝试像这样使用它,这样您的组件将获得 redux 存储的状态和功能:

import React from 'react';
import { connect } from 'react-redux';
import { callAction } from '../redux/actions.js';

const Home = (props) => {
  return (
    <div> {JSON.stringify(props)} </div>
  )
}

const mapState = (state) => {
  name : state.name // name is in intialState
}

const mapDispatch = (dispatch) => {
  callAction : () => dispatch(callAction()) // callAction is a redux action 
  //and should be imported in the component also
}

export default connect(mapState , mapDispatch)(Home);

您可以通过组件道具从您的 redux 商店访问状态和操作。

使用 useSelector() 而不是 useState()。示例 codepen 已修复。

更改自:

const { posts: { items: posts } } = useStore().getState();

更改为:

const posts = useSelector(state => state.posts.items);

useStore() 值仅在首次安装组件时收到。而 useSlector() 将在值更改时获取值。