javascript - 用钩子反应上下文防止重新渲染
问题描述
我使用带有钩子的 React 上下文作为我的 React 应用程序的状态管理器。每次商店中的值发生变化时,所有组件都会重新渲染。
有什么办法可以防止 React 组件重新渲染?
存储配置:
import React, { useReducer } from "react";
import rootReducer from "./reducers/rootReducer";
export const ApiContext = React.createContext();
export const Provider = ({ children }) => {
const [state, dispatch] = useReducer(rootReducer, {});
return (
<ApiContext.Provider value={{ ...state, dispatch }}>
{children}
</ApiContext.Provider>
);
};
一个减速器的例子:
import * as types from "./../actionTypes";
const initialState = {
fetchedBooks: null
};
const bookReducer = (state = initialState, action) => {
switch (action.type) {
case types.GET_BOOKS:
return { ...state, fetchedBooks: action.payload };
default:
return state;
}
};
export default bookReducer;
根减速器,可以组合尽可能多的减速器:
import userReducer from "./userReducer";
import bookReducer from "./bookReducer";
const rootReducer = ({ users, books }, action) => ({
users: userReducer(users, action),
books: bookReducer(books, action)
});
一个动作的例子:
import * as types from "../actionTypes";
export const getBooks = async dispatch => {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1", {
method: "GET"
});
const payload = await response.json();
dispatch({
type: types.GET_BOOKS,
payload
});
};
export default rootReducer;
这是书籍组件:
import React, { useContext, useEffect } from "react";
import { ApiContext } from "../../store/StoreProvider";
import { getBooks } from "../../store/actions/bookActions";
const Books = () => {
const { dispatch, books } = useContext(ApiContext);
const contextValue = useContext(ApiContext);
useEffect(() => {
setTimeout(() => {
getBooks(dispatch);
}, 1000);
}, [dispatch]);
console.log(contextValue);
return (
<ApiContext.Consumer>
{value =>
value.books ? (
<div>
{value.books &&
value.books.fetchedBooks &&
value.books.fetchedBooks.title}
</div>
) : (
<div>Loading...</div>
)
}
</ApiContext.Consumer>
);
};
export default Books;
当 Books 组件中的值发生变化时,另一个我的组件 Users 重新呈现:
import React, { useContext, useEffect } from "react";
import { ApiContext } from "../../store/StoreProvider";
import { getUsers } from "../../store/actions/userActions";
const Users = () => {
const { dispatch, users } = useContext(ApiContext);
const contextValue = useContext(ApiContext);
useEffect(() => {
getUsers(true, dispatch);
}, [dispatch]);
console.log(contextValue, "Value from store");
return <div>Users</div>;
};
export default Users;
优化上下文重新渲染的最佳方法是什么?提前致谢!
解决方案
Books
并且当前在每个周期Users
重新渲染- 不仅在存储值更改的情况下。
1.道具和状态变化
React重新渲染整个子组件树,从组件作为根开始,其中 props 或 state 发生了变化。您将父状态更改为getUsers
,Books
然后Users
重新渲染。
const App = () => {
const [state, dispatch] = React.useReducer(
state => ({
count: state.count + 1
}),
{ count: 0 }
);
return (
<div>
<Child />
<button onClick={dispatch}>Increment</button>
<p>
Click the button! Child will be re-rendered on every state change, while
not receiving any props (see console.log).
</p>
</div>
);
}
const Child = () => {
console.log("render Child");
return "Hello Child ";
};
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
优化技术
如果它自己的道具实际上没有改变,则用于React.memo
防止重新渲染一个组合。
// prevents Child re-render, when the button in above snippet is clicked
const Child = React.memo(() => {
return "Hello Child ";
});
// equivalent to `PureComponent` or custom `shouldComponentUpdate` of class comps
重要提示: React.memo
仅检查道具更改(useContext
值更改触发重新渲染)!
2. 上下文变化
当上下文值更改时,所有上下文使用者 ( useContext
) 都会自动重新呈现。
// here object reference is always a new object literal = re-render every cycle
<ApiContext.Provider value={{ ...state, dispatch }}>
{children}
</ApiContext.Provider>
优化技术
确保对上下文值有稳定的对象引用,例如通过useMemo
Hook。
const [state, dispatch] = useReducer(rootReducer, {});
const store = React.useMemo(() => ({ state, dispatch }), [state])
<ApiContext.Provider value={store}>
{children}
</ApiContext.Provider>
其他
不确定,为什么将所有这些结构放在一起Books
,只使用一个useContext
:
const { dispatch, books } = useContext(ApiContext);
// drop these
const contextValue = useContext(ApiContext);
<ApiContext.Consumer> /* ... */ </ApiContext.Consumer>;
您还可以使用和来查看此代码示例。React.memo
useContext
推荐阅读
- android - Firebase 库必须使用确切的版本错误
- ruby-on-rails - 在 ruby 中传递带有参数的方法作为参数
- php - scandir() 以什么顺序加载文件?
- java - Exiftool 没有打印到 ProcessBuilder 中的文件?
- java - 使用服务生成器将数据库数据显示到数据表
- algorithm - 分布式算法的空间复杂度
- angular - Angular 5 中具有不同输入的反应式表单和数学运算
- python - 从两列(标签、文本)数据源进行文本分类从哪里开始?
- jquery - 在打字稿中使用 $.get 的成功回调时无法将数据绑定到类变量
- datetimepicker - 如何将最小日期设置为 datetimepicker