Вопросы?
Мемоизация — сохранение результатов выполнения функций для предотвращения повторных вычислений. Это один из способов оптимизации, применяемый для увеличения скорости выполнения компьютерных программ.
Когда нужна мемоизация?
https://habr.com/ru/company/ruvds/blog/332384/
https://underscorejs.org/docs/underscore.html#section-82
Селектор - функция (чистая), которая вытягивает из state определенные данные
const selectNextState = (state) => state.nextMove;
Имеет смысл выносить часть логики в селекторы, потому что:
Reselect позволяет создавать селекторы, которые обладают следующими свойствами:
/* one selector */
export function createSelector<S, R1, T>(
selector: Selector<S, R1>,
combiner: (res: R1) => T
): OutputSelector<S, T, (res: R1) => T>;
export function createSelector<S, P, R1, T>(
selector: ParametricSelector<S, P, R1>,
combiner: (res: R1) => T
): OutputParametricSelector<S, P, T, (res: R1) => T>;
/* two selectors */
export function createSelector<S, R1, R2, T>(
selector: Selector<S, R1>,
selector: Selector<S, R2>,
combiner: (res1: R1, res2: R2) => T
): OutputSelector<S, T, (res1: R1, res2: R2) => T>;
export function createSelector<S, P, R1, R2, T>(
selector: ParametricSelector<S, P, R1>,
selector: ParametricSelector<S, P, R2>,
combiner: (res1: R1, res2: R2) => T
): OutputParametricSelector<S, P, T, (res1: R1, res2: R2) => T>;
const getFilledCellsCount = (state) => countFiledCells(state.gameField);
const getFilledCellsCountFactorial = createSelector(
getFilledCellsCount,
(count) => getFactorial(count)
);
При этом можно использовать параметризованные селекторы:
export const getOrderById = createSelector(
getAllOrders, // { [key: number] : OrderDetails }
// https://github.com/reduxjs/reselect/blob/v4.0.0/typescript_test/test.ts#L107
(_state, orderId: number) => orderId,
(ordersByid, orderId): Optional<OrderDetails> => ordersById[orderId]
);
НО! нужно помнить, что размер кэша у селекторов из createSelector === 1
https://github.com/reduxjs/reselect#api
https://github.com/reduxjs/reselect#use-memoize-function-from-lodash-for-an-unbounded-cache
function someActionCreator1(payload: { name: string; age: number }) {
return {
type: "SOME_ACTION",
payload,
};
}
const someActionCreator2 = createAction<{ name: string; age: number }>(
"SOME_ACTION"
);
if (action.type === "SOME_ACTION") {
action.payload. // nothing here
}
if (someActionCreator2.match(action)) {
action.payload. // we know the payload
}
function counter(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
}
const counter = createReducer(0, {
[increment]: (state) => state + 1,
[decrement]: (state) => state - 1,
});
const reducer = createReducer(
{
totalAge: 0,
names: "",
},
{
[someActionCreator2.type]: (state, action) => ({
totalAge: state.totalAge + action.payload.age,
names: state.names + " " + action.payload.name,
}),
}
);
import produce from "immer";
const baseState = [
{
todo: "Learn typescript",
done: true,
},
{
todo: "Try immer",
done: false,
},
];
const nextState = produce(baseState, (draftState) => {
draftState.push({ todo: "Tweet about it" });
draftState[1].done = true;
});
https://github.com/immerjs/immer
https://immerjs.github.io/immer/docs/introduction
const reducer = createReducer(
{
totalAge: 0,
names: "",
},
{
[someActionCreator2.type]: (state, action) => {
state.totalAge += action.payload.age;
state.names += ", " + action.payload.name;
return state;
},
}
);
https://redux-toolkit.js.org/api/createReducer#direct-state-mutation
export declare interface Slice<
State = any,
CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
Name extends string = string
> {
/**
* The slice name.
*/
name: Name;
/**
* The slice's reducer.
*/
reducer: Reducer<State>;
/**
* Action creators for the types of actions that are handled by the slice reducer.
*/
actions: CaseReducerActions<CaseReducers>;
/**
* The individual cae reudcer functions that were passed in the `reducers` parameter.
* This enables reuse and testing if they were defined inline when calling `createSlice`.
*/
caseReducers: SliceDefinedCaseReducers<CaseReducers>;
}
const counterSlice = createSlice({
name: "counter",
initialState: 0,
reducers: {
increment: (state) => state + 1,
decrement: (state) => state - 1,
},
});
const store = configureStore({
reducer: counterSlice.reducer,
});
document.getElementById("increment").addEventListener("click", () => {
store.disptch(counterSlice.actions.increment());
});
https://redux-toolkit.js.org/tutorials/basic-tutorial#introducing-createslice
https://github.com/reduxjs/redux-toolkit/blob/master/package.json#L60
type ConfigureEnhancersCallback = (
defaultEnhancers: StoreEnhancer[]
) => StoreEnhancer[];
interface ConfigureStoreOptions<S = any, A extends Action = AnyAction> {
// A single reducer function that will be used as the root reducer, or an
// object of slice reudcers that will passed to `combineReducers()`
reducer: Reducer<S, A> | ReducersMapObject<S, A>;
// An array of Redux middleware to install
middleware?: Middleware<{}, S>[];
// Whether to enable Redux DevTools integration. Defaults to `true`.
// Additional configuration can be done by passing Redux Devtools options
devTools?: boolean | DevToolsOptions;
// the initial state, same as Redux's createStore.
preloadedState?: DeepPartial<S extends any ? S : S>;
// The store enhancers to apply. See Redux's `createStore()`.
enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback;
}
Стоит почитать