我正在實施一個管理對話的所有訊息的背景關系。
為了降低演算法的復雜性,我決定使用 Map“sectionsRef”來訪問 O(1) 中的一些內容。
這個地圖需要在我的減速器邏輯中更新,我更新有狀態資料,以便同步兩者。
export function MessagesProvider({ children }) {
const [messages, dispatch] = useReducer(messagesReducer, initialState);
const sectionsRef = useMemo(() => new Map(), []);
const addMessages = (messages, unshift = false) => {
dispatch(actionCreators.addMessages(messages, unshift));
};
const addMessage = (message) => addMessages([message]);
const deleteMessage = (messageId) => {
dispatch(actionCreators.deleteMessage(messageId));
};
const value = useMemo(() => ({
messages,
addMessages,
deleteMessage,
// eslint-disable-next-line react-hooks/exhaustive-deps
}), [messages]);
return (
<MessagesContext.Provider value={value}>
{children}
</MessagesContext.Provider>
);
}
如您所見,我在初始化 Map 時使用了 useMemo,以防止由于重新渲染而重新初始化。
將它作為有效負載傳遞給我的減速器操作是否正確?
const addMessages = (messages, unshift = false) => {
dispatch(actionCreators.addMessages(messages, unshift, sectionsRef)); <---
};
為了簡化我的問題,想象一下這是真正的代碼:
//
// Reducer action
//
function reducerAction(state, messages, sectionsRef, title) {
state.push(...messages);
sectionsRef.set(title, state.length - 1);
}
//
// Context code
//
const state = [];
const firstMessagesSection = [{ id: 1 }];
const secondMessagesSection = [{ id: 1 }, { id: 2 }]
const sectionsRef = new Map();
reducerAction(state, firstMessagesSection, sectionsRef, "first section");
reducerAction(state, secondMessagesSection, sectionsRef, "second section");
console.log(state);
console.log(sectionsRef.get("second section"));
我問這個是因為我已經讀到我們不應該在減速器邏輯中運行副作用......所以,如果我需要將該地圖與狀態同步,我應該怎么做?
uj5u.com熱心網友回復:
將它作為有效負載傳遞給我的減速器操作是否正確?
否:reducer 必須是純函式。
Redux使用我認為非常有用的簡短串列描述了reducer:
減速器的規則
我們之前說過,reducer 必須始終遵循一些特殊規則:
- 他們應該只根據
state和action引數計算新的狀態值- 他們不允許修改現有的
state. 相反,他們必須通過復制現有值并對復制的值進行更改來進行不可變的更新state。- 他們不能做任何異步邏輯或其他“副作用”
第二項和第三項一起描述純函式,第一項只是 Redux 特定的約定。
在您的示例中,您違反了純函式的兩條規則:
- 改變狀態
state.push(...messages)(而不是創建一個新陣列并回傳它),以及 - 通過修改外部作用域中的變數來執行副作用:
sectionsRef.set(title, state.length - 1)
此外,您似乎從未使用過Map(如何在您的程式中訪問它?)。它應該包含在你的背景關系中,你可以簡單地在你的組件之外定義它(它的身份永遠不會改變,所以它不會導致重新渲染)。
以下是重構代碼以實作目標的方法:
保持reducer資料純凈:
// store.js
export function messagesReduer (messages, action) {
switch (action.type) {
case 'ADD': {
const {payload, unshift} = action;
return unshift ? [...payload, ...messages] : [...messages, ...payload];
}
case 'DELETE': {
const {payload} = action;
return messages.filter(m => m.id !== payload);
}
}
}
export const creators = {};
creators.add = (messages, unshift = false) => ({type: 'ADD', payload: messages, unshift});
creators.delete = (id) => ({type: 'DELETE', payload: id});
export const sections = new Map();
Map通過將這些操作組合到一個函式中,在將操作分派到相關狀態的同時更新:
// MessagesContext.jsx
import {
createContext,
useCallback,
useMemo,
useReducer,
} from 'react';
import {
creators,
messagesReduer,
sections,
} from './store';
export const MessagesContext = createContext();
export function MessagesProvider ({ children }) {
const [messages, dispatch] = useReducer(messagesReducer, []);
const addMessages = useCallback((title, messages, unshift = false) => {
dispatch(creators.add(messages, unshift));
sections.set(title, messages.length);
}, [creators.add, dispatch, messages]);
const addMessage = useCallback((title, message, unshift = false) => {
dispatch(creators.add([message], unshift));
sections.set(title, messages.length);
}, [creators.add, dispatch, messages]);
const deleteMessage = useCallback((id) => {
dispatch(creators.delete(id));
}, [creators.delete, dispatch]);
const value = useMemo(() => ({
addMessage,
addMessages,
deleteMessage,
messages,
sections,
}), [
addMessage,
addMessages,
deleteMessage,
messages,
sections,
]);
return (
<MessagesContext.Provider value={value}>
{children}
</MessagesContext.Provider>
);
}
使用背景關系:
// App.jsx
import {useContext} from 'react';
import {MessagesContext, MessagesProvider} from './MessagesContext';
function Messages () {
const {
// addMessage,
// addMessages,
// deleteMessage,
messages,
// sections,
} = useContext(MessagesContext);
return (
<ul>
{
messages.map(({id}, index) => (
<li key={id}>Message no. {index 1}: ID {id}</li>
))
}
</ul>
);
}
export function App () {
return (
<MessagesProvider>
<Messages />
</MessagesProvider>
);
}
補充筆記:
- 確保您的依賴項串列(例如 in
useMemo等)是詳盡無遺的。這些 lint 警告是為了幫助防止您犯錯誤。一般來說,您永遠不需要壓制它們。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/393351.html
標籤:javascript 反应 反应原生 反应钩子
