我有两个非常相似的功能(在HTML画布上的AI"修复"one_answers"绘制"(。他们每个人在应用程序中都有一个选项卡。修复工作良好,用户群正在使用,而输出是当前的在制品。
这些特性具有几乎相同(但并非微不足道(的组件和状态,当然我希望尽可能多地共享。
我有这个工作,但我对打字感到困惑,我也不确定这是否是解决问题的好方法。
设置如下:
为了共享减速器逻辑和初始状态,根据RTK指南,我将createSlice
包装为createGenericCanvasSlice
:https://redux-toolkit.js.org/usage/usage-with-typescript#wrapping-createslice
export const createGenericCanvasSlice = <
Reducers extends SliceCaseReducers<GenericCanvasState>
>({
name = '',
initialState,
reducers,
}: {
name: string;
initialState: GenericCanvasState;
reducers: ValidateSliceCaseReducers<GenericCanvasState, Reducers>;
}) => {
return createSlice({
name,
initialState,
reducers: {
...genericCanvasReducers, // <--- the generic reducers below
...reducers,
},
});
};
通用减速器对象如下所示:
export const genericCanvasReducers = {
setTool: (
state: GenericCanvasState,
action: PayloadAction<InpaintingTool>
) => {
state.tool = action.payload;
},
toggleTool: (state: GenericCanvasState) => {
state.tool = state.tool === 'brush' ? 'eraser' : 'brush';
},
setBrushSize: (state: GenericCanvasState, action: PayloadAction<number>) => {
state.brushSize = action.payload;
},
// etc
};
然后,每个功能都得到了自己的切片,如下所示:
const inpaintingSlice = createGenericCanvasSlice({
name: 'inpainting',
initialState: initialInpaintingState as GenericCanvasState,
reducers: {
// addition reducers here if needed
},
});
const outpaintingSlice = createGenericCanvasSlice({
name: 'outpainting',
initialState: initialOutpaintingState as GenericCanvasState,
reducers: {
// addition reducers here if needed
},
});
这很好;我可以为createGenericCanvasSlice
创建的切片调度操作,但我必须提前知道我想要的是修复操作还是outpainting操作。
为了解决这个问题,我为每个通用操作生成了thunk,它们接受第二个参数(当前选项卡名称(。在thunk中,检查选项卡名称并将适当的操作分派到适当的切片。
/**
* Given the two possible actions, returns a thunk that accepts a payload and tab name
* and dispatches the appropriate action to the appropriate slice.
*/
const createCanvasThunk =
(inpaintingAction, outpaintingAction) =>
(payload, activeTabName) =>
(dispatch, getState) => {
if (activeTabName === 'inpainting') {
dispatch(inpaintingAction(payload));
} else if (activeTabName === 'outpainting') {
dispatch(outpaintingAction(payload));
}
};
/**
* Creates an object with all the thunks, where the key is the action name.
*/
export const canvasThunks = _.reduce(
genericCanvasReducers,
(thunks, _action, key) => {
thunks[key] = createCanvasThunk(
inpaintingActions[key],
outpaintingActions[key]
);
return thunks;
},
{}
);
这是有效的,但我无法计算出createCanvasThunk
和reduce
回调的类型。
我最终使用了一个简单得多的解决方案。我制作了一个名为canvas
的新状态切片,并赋予它属性inpainting
和outpainting
,它们都有通用画布状态的副本。
我给了canvas
切片另一个属性currentCanvas
,即"inpainting" | "outpainting"
:
export interface CanvasState {
currentCanvas: ValidCanvasName;
inpainting: GenericCanvasState;
outpainting: GenericCanvasState;
}
我为每个reducer添加了逻辑,以使用currentCanvas
作为基本canvas
切片的索引,然后执行相同的逻辑。
setTool: (state, action: PayloadAction<InpaintingTool>) => {
state[state.currentCanvas].tool = action.payload;
},
最后一步是更新我的所有选择器。我从新的canvasSlice
中导出了一个方便选择器,以简单地获得当前活动画布:
export const currentCanvasSelector = (state: RootState) =>
state.canvas[state.canvas.currentCanvas];
我的选择器现在看起来是这样的:
const inpaintingBrushSelector = createSelector(
[currentCanvasSelector, activeTabNameSelector, areHotkeysEnabledSelector],
(currentCanvas: GenericCanvasState, activeTabName, areHotkeysEnabled) => {
const { tool, brushSize, shouldShowMask } = currentCanvas;
return {
tool,
brushSize,
shouldShowMask,
activeTabName,
areHotkeysEnabled,
};
},
{
memoizeOptions: {
resultEqualityCheck: _.isEqual,
},
}
);
我们可能希望将一些状态(例如画布画笔半径(从inpainting
/outpainting
级别提升到基本canvas
级别。然后,我们只需从适当的减速器中删除额外的currentCanvas
索引。
我们可能希望对部件内部进行特殊处理,具体取决于我们是在进行修补还是外涂。这将通过组件内的简单条件检查来处理。
因此,最终结果非常简单,我们现在只需要对特征出现分歧的区域进行特殊处理。