我正在使用useMemo钩子返回并过滤项目数组。然后,我有一个切换函数,可以切换一个项目是真还是假,然后将该项目发布回 API(如果它是真还是假(并将其添加到列表中。在使用 useReducer 钩子的函数中,数组落后一步。例如,返回项目数组,您切换它们是否在销售中,如果您切换 true,它们将被添加到销售列表中,如果它们被切换为不销售,它们将被添加到 notSaleList。在函数中,saleList 长度将返回为 3,但实际上是 4,然后您删除一个房屋以使其成为 3,但它将返回 4。有谁知道为什么要感谢?
const homesReducer = (state, action) => {
switch (action.type) {
case 'FETCH_INIT':
return {
...state,
isLoading: true,
isError: false,
};
case 'FETCH_SUCCESS':
//action.payload to object
const entities = action.payload.reduce((prev, next) => {
return { ...prev, [next.Id]: next };
}, {});
return {
...state,
isLoading: false,
isError: false,
homes: entities,
};
case 'FETCH_FAILURE':
return {
...state,
isLoading: false,
isError: true,
};
case 'TOGGLE_SALE_HOME_INIT':
return {
...state,
homes: {
...state.homes,
// ask Jenkins
[action.payload]: {
...state.homes[action.payload],
IsSaleHome: !state.homes[action.payload].IsSaleHome,
},
},
};
case 'TOGGLE_SALE_HOME_SUCCESS':
return {
...state,
};
case 'TOGGLE_SALE_HOME_FAILURE':
// TODO update back if failed
return {
...state,
homes: {
...state.homes,
// ask Jenkins
[action.payload]: {
...state.homes[action.payload],
IsSaleHome: !state.homes[action.payload].IsSaleHome,
},
},
};
default:
return { ...state };
}
};
const useOnDisplayApi = activeLotNumber => {
const [state, dispatch] = useReducer(homesReducer, {
isLoading: false,
isError: false,
homes: [],
saleHomes: [],
});
const homes = useMemo(() => {
return Object.keys(state.homes).map(id => {
return state.homes[id];
});
}, [state.homes]);
}
const saleHomes = useMemo(() => {
return homes.filter(home => {
return home.IsSaleHome;
});
}, [homes]);
const notSaleHomes = useMemo(() => {
return homes.filter(home => {
return !home.IsSaleHome && !home.IsSuggestedSaleHome;
});
}, [homes]);
const toggleSaleHome = async (home, undo = true) => {
dispatch({ type: 'TOGGLE_SALE_HOME_INIT', payload: home.Id });
try {
const didUpdate = await updateInventory(
activeLotNumber,
home.InventoryId,
{
InventoryId: home.InventoryId,
IsSaleHome: !home.IsSaleHome,
}
);
if (didUpdate == true) {
dispatch({ type: 'TOGGLE_SALE_HOME_SUCCESS' });
}
else {
dispatch({ type: 'TOGGLE_SALE_HOME_FAILURE', payload: home.Id });
}
} catch (error) {
setTimeout(() => {
dispatch({ type: 'TOGGLE_SALE_HOME_FAILURE' });
}, 600);
}
};
调度后的更新不会立即可用,并且是异步的。因此,你的应用将经历另一个呈现周期来反映更新。
您需要使用 useEffect
在更新后调用 api,而不是在初始渲染时调用它。
const initialRender = useRef(true);
useEffect(() => {
if(initialRender.current) {
initialRender.current = false;
} else {
try {
const didUpdate = await updateInventory(
activeLotNumber,
home.InventoryId,
{
InventoryId: home.InventoryId,
IsSaleHome: !home.IsSaleHome,
}
);
if (didUpdate == true) {
dispatch({ type: 'TOGGLE_SALE_HOME_SUCCESS' });
}
else {
dispatch({ type: 'TOGGLE_SALE_HOME_FAILURE', payload: home.Id });
}
} catch (error) {
setTimeout(() => {
dispatch({ type: 'TOGGLE_SALE_HOME_FAILURE' });
}, 600);
}
}
}, [home])
const toggleSaleHome = async (home, undo = true) => {
dispatch({ type: 'TOGGLE_SALE_HOME_INIT', payload: home.Id });
}
我最终通过在"INIT"之前添加 if 语句来解决我的问题。
const toggleSaleHome = async (home, undo = true) => {
if (saleHomes.length > 9 && !home.IsSaleHome) {
toast.error(
<div>
{`${home.Name} could not be added. You already have selected 10 sale homes.`}
</div>,
{
className: 'error-toast',
progressClassName: 'error-progress-bar',
closeButton: false,
position: toast.POSITION.BOTTOM_RIGHT,
}
);
return;
}
dispatch({ type: 'TOGGLE_SALE_HOME_INIT', payload: home.Id });
try {
const didUpdate = await updateInventory(
activeLotNumber,
home.InventoryId,
{
InventoryId: home.InventoryId,
IsSaleHome: !home.IsSaleHome,
}
);
if (didUpdate == true) {
dispatch({ type: 'TOGGLE_SALE_HOME_SUCCESS' });
}
else {
dispatch({ type: 'TOGGLE_SALE_HOME_FAILURE', payload: home.Id });
}
} catch (error) {
setTimeout(() => {
dispatch({ type: 'TOGGLE_SALE_HOME_FAILURE' });
}, 600);
}
};
我的整个问题是,我不希望更多的房屋能够在它们达到 10 个并且"INIT"之前能够切换 saleHomes 的实际状态可用,因此 saleHomes.length 是准确的。