reactjs - 如何处理具有多种状态和动作类型的化简器和上下文
问题描述
我正在创建一个小应用程序来熟悉 React。我正在尝试遵循这篇 Kent C Dodds文章中介绍的模式。但我有比文章更多的状态。
这是我的初始状态
{
status // the loading states of the app (pending/ resolved/ rejected)
error // any error that occurred
profiles // the profile list
profile // when editing the profile selected to edit
formType // Create or update form type
}
我已将应用程序创建到工作级别,并尝试优化我的解决方案以使其更具可扩展性。因为我在一个 reducer 中有很多动作类型。
这是我的上下文、提供者和减速器(我只添加了与异步操作相关的操作类型)
const initialState = {
status: null,
error: null,
profiles: [],
profile: null,
formType: 'Create'
}
function profileReducer(state, action) {
switch (action.type) {
case 'LOADING_START': {
return {
...state,
status: 'pending',
}
}
case 'LOADING_FAILED': {
return {
...state,
status: 'rejected',
error: action.payload
}
}
case 'LOADING_SUCCESS': {
return {
...state,
status: 'resolved',
}
}
case 'PROFILES_GET': {
return {
...state,
profiles: action.payload,
}
}
case 'FORM_TYPE_UPDATE': {
return {
...state,
formType: action.payload,
}
}
case 'PROFILE_EDIT': {
return {
...state,
profile: action.payload,
}
}
default: {
throw new Error(`Unhandled action type: ${action.type}`)
}
}
}
function ProfileProvider({ children }) {
const [state, dispatch] = React.useReducer(profileReducer, initialState)
const value = [state, dispatch];
return <ProfileContext.Provider value={value}>{children}</ProfileContext.Provider>
}
function useProfile() {
const context = React.useContext(ProfileContext)
if (context === undefined) {
throw new Error('useProfile must be used within a ProfileProvider')
}
return context
}
async function getProfiles(dispatch) {
dispatch({ type: 'LOADING_START' })
try {
const profiles = await client.getProfiles()
dispatch({ type: 'LOADING_SUCCESS' })
dispatch({ type: 'PROFILES_GET', payload: profiles })
} catch (error) {
dispatch({ type: 'LOADING_FAILED', payload: error })
throw error;
}
}
async function addProfile(dispatch, profile) {
dispatch({ type: 'LOADING_START'})
try {
console.log({ profile });
const addedProfile = await client.addProfile(profile)
const profiles = await client.getProfiles()
dispatch({ type: 'PROFILES_GET', payload: profiles })
dispatch({ type: 'LOADING_SUCCESS' })
} catch (error) {
dispatch({ type: 'LOADING_FAILED', payload: error })
throw error;
}
}
async function updateProfile(dispatch, profile) {
dispatch({ type: 'LOADING_START' })
try {
console.log({ profile });
const updatedProfile = await client.updateProfile(profile)
const profiles = await client.getProfiles()
dispatch({ type: 'PROFILES_GET', payload: profiles })
dispatch({ type: 'LOADING_SUCCESS' })
} catch (error) {
dispatch({ type: 'LOADING_FAILED', payload: error })
throw error;
}
}
async function deleteProfile(dispatch, profile) {
dispatch({ type: 'LOADING_START' })
try {
console.log({ profile });
const updatedProfile = await client.deleteProfile(profile)
const profiles = await client.getProfiles()
dispatch({ type: 'PROFILES_GET', payload: profiles })
dispatch({ type: 'LOADING_SUCCESS' })
} catch (error) {
dispatch({ type: 'LOADING_FAILED', payload: error })
throw error;
}
}
export {
ProfileProvider,
useProfile,
addProfile
}
这是我使用上下文的表单
function Form() {
const { register, handleSubmit } = useForm();
const [{ formType, profile }, profileDispatch] = useProfile();
const buttonText = formType === 'Create' ? 'Create' : 'Edit';
React.useEffect(() => {
if(formType === 'Edit'){
// populate input values
...
}
}, [formType])
const onSubmit = data => {
if (formType === 'Create') {
addProfile(profileDispatch, data).catch(error => {
toast(error);
})
} else {
updateProfile(profileDispatch, data).catch(error => {
toast(error);
})
}
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name", { required: true })} />
<input {...register("age", { required: true })} />
<button>{buttonText}</button>
</form>
)
}
解决方案
推荐阅读
- scenekit - 如何从 Autodesk Maya 生成阴影 png 文件?
- ios - WKWebView iOS 13 中的 DeviceMotion 和 DeviceOrientation
- java - 设计审查:带有“版本切换”的 Spring JPA 实体版本控制
- android - 数据绑定:将 LiveData 原样传递给 BindingAdapter 并使用 LifecycleOwner 观察它
- regex - 模式匹配后的下一个单词
- verification - 在dafny中验证条件下的序列和
- android - MaterialDatePicker : 今天如何设置自定义
- c# - 使用 roslyn 在 c# 源代码中查找类引用
- angular - 量角器:为所有 browser.wait 调用设置超时
- c# - 使用 DisplayModeProvider 指定移动视图的 ASP.NET 应用程序启动时要转到的路由