@HMR 使用 thunk 很好,但我不喜欢他们对减速器所做的事情。你已经在使用 redux-toolkit,这很棒! redux-toolkit 实际上包含并导出了一个nanoid 函数,他们在幕后使用该函数为thunk 创建唯一的ID。你可以用它来代替Math.random() * 100000。
我总是从考虑类型开始。什么是Alert?您不想存储<Alert/>,因为JSX.Element 不可序列化。相反,您应该只存储道具。你肯定会存储message 和key/id。如果您在前端处理过期,那么您还将存储 duration,但如果过期由 thunk 处理,那么我认为您在 redux 状态或组件道具中不需要它。
您似乎希望一次允许多个警报,因此return payload 不会为您的减速器削减它。您需要存储一个数组或一个键控对象来存储所有活动警报。
你绝对不应该在减速器中使用setTimeout,因为这是一个副作用。您可以在 thunk 或 useEffect 组件中的 useEffect 中使用它。我倾向于该组件,因为警报似乎也应该可以忽略?因此,您可以使用相同的函数来处理关闭点击和自动超时。
我们可以定义要为每个警报存储的信息。
type AlertData = {
message: string;
id: string;
duration: number;
}
以及我们创建该警报所需的信息,它们是相同的,但没有id,因为我们将在reducer 中生成id。
type AlertPayload = Omit<AlertData, 'id'>
我们的状态可以是一个警报数组:
const initialState: AlertData[] = [];
我们需要采取措施来添加新警报并在警报过期后删除它。
import { createSlice, PayloadAction, nanoid } from "@reduxjs/toolkit";
...
export const alertSlice = createSlice({
name: "alert",
initialState,
reducers: {
addAlert: (state, { payload }: PayloadAction<AlertPayload>) => {
const id = nanoid(); // create unique id
state.push({ ...payload, id }); // add to the state
},
removeAlert: (state, { payload }: PayloadAction<string>) => {
// filter the array -- payload is the id
return state.filter((alert) => alert.id !== payload);
}
}
});
export const { addAlert, removeAlert } = alertSlice.actions;
export const alertReducer = alertSlice.reducer;
现在到组件了。我的想法是,您将使用选择器来选择所有警报,然后每个警报将负责自己的到期。
export const AlertComponent = ({ message, duration, id }: AlertData) => {
const dispatch = useDispatch();
// function called when dismissed, either by click or by timeout
// useCallback is just so this can be a useEffect dependency and won't get recreated
const remove = useCallback(() => {
dispatch(removeAlert(id));
}, [dispatch, id]);
// automatically expire after the duration, or if this component unmounts
useEffect(() => {
setTimeout(remove, duration);
return remove;
}, [remove, duration]);
return (
<Alert
onClose={remove} // can call remove directly by clicking the X
dismissible
>
<Alert.Heading>Alert!</Alert.Heading>
<p>{message}</p>
</Alert>
);
};
export const ActiveAlerts = () => {
const alerts = useSelector((state) => state.alerts);
return (
<>
{alerts.map((props) => (
<AlertComponent {...props} key={props.id} />
))}
</>
);
};
我还制作了一个组件来创建警报来测试它并确保它有效!
export const AlertCreator = () => {
const dispatch = useDispatch();
const [message, setMessage] = useState("");
const [duration, setDuration] = useState(8000);
return (
<div>
<h1>Create Alert</h1>
<label>
Message
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</label>
<label>
Duration
<input
type="number"
step="1000"
value={duration}
onChange={(e) => setDuration(parseInt(e.target.value, 10))}
/>
</label>
<button
onClick={() => {
dispatch(addAlert({ message, duration }));
setMessage("");
}}
>
Create
</button>
</div>
);
};
const App = () => (
<div>
<AlertCreator />
<ActiveAlerts />
</div>
);
export default App;
Code Sandbox Link