תרגיל טייפסקריפט: פעולת עדכון גנרית ב Redux
בדוגמת ההתחלה המהירה של Redux Toolkit הם מציעים את הקוד הבא עבור סלייס של מונה:
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
לא מפתיע שזה מתאים בדיוק לתפיסת העולם של Redux - מעט מידע, שניתן לשינוי במספר דרכים (פעולות), כל דרך שינוי עם המגבלות והבדיקות שלה. אבל בעולם האמיתי יש עוד מקרה נפוץ שלא מדברים עליו בתיעוד שלהם, וזה אוביקט עם הרבה מידע שמאפשר שינויים יחסית גמישים. דמיינו את אוביקט המידע הבא:
export interface CounterSliceState {
value: number
status: "idle" | "loading" | "failed"
foo: string
bar: number
buz: Array<string>
}
ואולי יהיו שם עוד 20 שדות מטיפוסים שונים. כתבו פעולת Set גנרית (אחת) שתקבל מפתח וערך מהסוג שמתאים לו ותכתוב את זה לאוביקט המידע.
1. פיתרון
בשביל הפיתרון עלינו תחילה לכתוב את ה Reducer. אפשר להגדיר משהו כזה:
extraReducers: (builder) => {
builder.addCase(setField, (state, action) => {
const { key, value } = action.payload;
// @ts-ignore
state[key] = value;
});
},
אני מוכן להתעלם כאן מבעיות טייפסקריפט כי הוולידציה מתרחשת קודם, בעת יצירת ה Action. הקוד הבא יוצר Action גנרי עבור כתיבה לשדה כלשהו באוביקט מידע:
type SetFieldPayload = {
[K in keyof CounterSliceState]: {
key: K;
value: CounterSliceState[K];
};
}[keyof CounterSliceState];
export const setField = createAction<SetFieldPayload2>('counter/set');
ועכשיו הקוד הזה מתקמפל:
setField({key: 'bar', value: 5}) // compiles OK
אבל זה זורק שגיאה:
setField({key: 'bar', value: '?'}) // compilation error
נ.ב. אפשר להרחיב את הפיתרון ולכתוב גם למפתח מקונן (בקשו מ Chat GPT את הקוד אם לא מצאתם את הרקורסיה), אבל אני לא ממליץ. ככל שטייפסקריפט נהיה מסובך מדי הוא מפסיק להיות שימושי, ובאופן כללי רידאקס עובד יותר טוב עם סלייסים שטוחים. יותר קל לקחת את החלק המקונן ולהפריד אותו לסלייס משלו.