React Redux 和 Context Api - 如何通过上下文传递道具并保留它 "connected"?
React Redux & ContextApi - How to pass a prop via context and keeping it "connected"?
考虑在使用上下文 API 的 react-redux 应用程序上设置以下项目以避免 prop drilling。给出的示例已简化。
项目设置
- React 项目使用 React Redux
- 在某些情况下使用上下文 API 来避免道具钻探。
- Redux 商店有一个道具
posts
,其中包含 posts 的列表
- 一个动作创建者
deletePost()
,它通过post id删除了某个post。
- 为了避免 prop drilling,
posts
和 deletePosts()
都被添加到上下文 AppContext
中并由钩子函数 useApp()
. 返回
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()
将在值更改时获取值。
考虑在使用上下文 API 的 react-redux 应用程序上设置以下项目以避免 prop drilling。给出的示例已简化。
项目设置
- React 项目使用 React Redux
- 在某些情况下使用上下文 API 来避免道具钻探。
- Redux 商店有一个道具
posts
,其中包含 posts 的列表
- 一个动作创建者
deletePost()
,它通过post id删除了某个post。 - 为了避免 prop drilling,
posts
和deletePosts()
都被添加到上下文AppContext
中并由钩子函数useApp()
. 返回
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()
将在值更改时获取值。