[英]React context with hooks prevent re render
我使用帶有鈎子的 React 上下文作為我的 React 應用程序的狀態管理器。 每次 store 中的值發生變化時,所有組件都會重新渲染。
有沒有辦法阻止 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;
Root reducer,可以組合盡可能多的reducer:
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 組件中的值發生變化時,另一個 my 組件 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
當前在每個周期重新渲染 - 不僅是在商店價值發生變化的情況下。
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
值更改觸發重新渲染)!
當上下文值更改時,所有上下文使用者 ( 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
查看此代碼示例。
我試圖用不同的例子來解釋希望會有所幫助。
因為上下文使用引用標識來確定何時重新渲染,所以當提供者的父級重新渲染時,這可能會在使用者中觸發意外渲染。
例如:下面的代碼將在每次 Provider 重新渲染時重新渲染所有消費者,因為總是為value
創建一個新對象
class App extends React.Component {
render() {
return (
<Provider value={{something: 'something'}}>
<Toolbar />
</Provider>
);
}
}
為了解決這個問題,將值提升到父狀態
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
這個用於防止組件在 React 中渲染的解決方案稱為 shouldComponentUpdate。 它是一種可在 React 類組件上使用的生命周期方法。 而不是像以前那樣將 Square 作為功能性無狀態組件:
const Square = ({ number }) => <Item>{number * number}</Item>;
您可以使用帶有 componentShouldUpdate 方法的類組件:
class Square extends Component {
shouldComponentUpdate(nextProps, nextState) {
...
}
render() {
return <Item>{this.props.number * this.props.number}</Item>;
}
}
如您所見, shouldComponentUpdate 類方法在運行組件的重新渲染之前可以訪問下一個 props 和 state。 這就是您可以決定通過從此方法返回 false 來阻止重新渲染的地方。 如果返回 true,組件將重新渲染。
class Square extends Component {
shouldComponentUpdate(nextProps, nextState) {
if (this.props.number === nextProps.number) {
return false;
} else {
return true;
}
}
render() {
return <Item>{this.props.number * this.props.number}</Item>;
}
}
在這種情況下,如果傳入的數字道具沒有改變,則組件不應更新。 通過將控制台日志再次添加到您的組件中來嘗試一下。 當透視改變時,Square 組件不應重新渲染。 這對您的 React 應用程序來說是一個巨大的性能提升,因為您的所有子組件都不會隨着其父組件的每次重新渲染而重新渲染。 最后,由您來阻止重新渲染組件。
了解這個 componentShouldUpdate 方法肯定會對你有所幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.