React ContextAPI:为什么商店数据的本地副本会更新?
React ContextAPI: why is a local copy of store data getting updated?
我正在使用 ContextAPI 来管理全局数据状态,以便与其 Provider 中包装的任何页面共享。在一种情况下,我需要复制商店中的内容并在本地进行更改,但不影响其他页面中使用的数据。但是,我的更新是直接更新全局商店数据。
在代码中,tmpStoreActivities.unshift(response); setCurrentActivity(tmpStoreActivities[0]);
正在更新商店的 activities
数组,即使 activities
被重新分配给其他变量两次,我不明白为什么。我是否误解了全局存储值的修改方式?
STORE - 通过 ActivitiesProvider
export const useActivitiesStore = () => {
const activitiesService = new ActivitiesService();
const [activitiesState, setActivitiesState] = useState(
initialActivitiesState
);
const { activities } = activitiesState;
//REDUCER
const setActivities = (newActivities) => {
setActivitiesState((prevState) => ({
...prevState,
...{ activities: newActivities }
}));
const getRecentActivities = useCallback(async (params?: {}) => {
setIsLoading(true);
resetActivities();
try {
//API call
const recentActivities = await activitiesService.getRecentActivities(
params
);
setActivities(recentActivities);
//OPTIONAL USAGE: use this version of the data ONLY when a separate local copy is needed
return recentActivities;
} catch (error) {
setError('Unable to Retrieve Activities');
} finally {
setIsLoading(false);
}
// disabling eslint because using this as dependency with dependencies causes infinite loop
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};
return {
activitiesState,
getRecentActivities
};
};
组件
import { useActivities } from '../../services/ActivitiesProvider';
export default function Activity() {
const { activitiesService, activitiesStore } = useActivities();
const { activitiesState, getRecentActivities } = activitiesStore;
const { activities, searchResults, isSearching } = activitiesState;
const [currentActivity, setCurrentActivity] = useState<IClassificationActivity>(null);
const [currentActivities, setCurrentActivities] = useState<IClassificationActivity[]>(null);
//caching the initial load of activities, so infinite loops don't occur if we getRecentActivities later
const storeActivities = useRef<IActivity[]>(isSearching ? searchResults : activities).current;
useEffect(() => {
//Get all the details for selected activity
const getActivityDetails = async () => {
const convertedType: number = type === ActivityType.Website ? 0 : 1;
const convertedId = parseInt(id);
try {
const response = await activityService.getClassificationActivityDetails(
convertedId,
convertedType
);
if (response) {
setActivityDetail(response);
if (storeActivities?.length) {
//if activities are pre-loaded from the Store, but the current activity does not exist in that array, add it to the front of the array and start the prev/next flow from there
const currActivity = storeActivities.filter(
(activity: IActivity) =>
activity.type === type && activity.id === parseInt(id)
)[0];
if (!currActivity) {
const tmpStoreActivities = storeActivities;
tmpStoreActivities.unshift(response);
setCurrentActivity(tmpStoreActivities[0]);
}
}
}
} catch (error) {
notificationService.error('Unable to load this Activity');
} finally {
setIsLoading(false);
}
};
if (id && id !== 'undefined' && id !== 'null' && type) {
getActivityDetails();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id, type, storeActivities, getRecentActivities]);
...
}
ROOT
<ActivitiesProvider>
<Switch>
<Route
path={Routes.Classification(':type', ':id')}
component={Activity}
exact
/>
</Switch>
</ActivitiesProvider>
上下文 API 用于在组件之间共享数据,而无需传递思想道具。这样做是为了避免道具钻井的问题。
上下文 API 所做的只是共享该信息,为了使您的对象不可变,您应该检查 Object.freeze() or some third party libraries like immutable。
当你有一个变量时,在 Javascript 中,当你将它重新分配给另一个变量时,你实际上并没有重新分配它,你只是通过引用指向它。
例如,当你有一个名为 myState
的状态时,它包含一个像这样的对象:
{
foo: "bar"
}
然后你做这样的事情:
export default function App() {
const [myState, setMyState] = useState({ foo: "bar" });
const myVar = myState;
myVar.foo = "foo";
console.log(myState); //Output: {foo: "foo"}
return (
<div className="App">
<div></div>
</div>
);
}
尽管重新分配,控制台日志的输出会告诉您 foo 的值实际上是“foo”。不过别担心,有一种方法可以防止这种情况,它被称为“传播”。
您可以像这样使用展开运算符复制对象:
const myStateCopy = {...myState};
现在,您不再引用 myState
。您可以在此 example. And please refer to the docs 中查看点差的工作原理以获取更多信息
编辑:
您也可以使用数组进行传播。
const myArrayState = [{foo: "bar"}];
const myArrayStateCopy = [...myArrayState];
我正在使用 ContextAPI 来管理全局数据状态,以便与其 Provider 中包装的任何页面共享。在一种情况下,我需要复制商店中的内容并在本地进行更改,但不影响其他页面中使用的数据。但是,我的更新是直接更新全局商店数据。
在代码中,tmpStoreActivities.unshift(response); setCurrentActivity(tmpStoreActivities[0]);
正在更新商店的 activities
数组,即使 activities
被重新分配给其他变量两次,我不明白为什么。我是否误解了全局存储值的修改方式?
STORE - 通过 ActivitiesProvider
export const useActivitiesStore = () => {
const activitiesService = new ActivitiesService();
const [activitiesState, setActivitiesState] = useState(
initialActivitiesState
);
const { activities } = activitiesState;
//REDUCER
const setActivities = (newActivities) => {
setActivitiesState((prevState) => ({
...prevState,
...{ activities: newActivities }
}));
const getRecentActivities = useCallback(async (params?: {}) => {
setIsLoading(true);
resetActivities();
try {
//API call
const recentActivities = await activitiesService.getRecentActivities(
params
);
setActivities(recentActivities);
//OPTIONAL USAGE: use this version of the data ONLY when a separate local copy is needed
return recentActivities;
} catch (error) {
setError('Unable to Retrieve Activities');
} finally {
setIsLoading(false);
}
// disabling eslint because using this as dependency with dependencies causes infinite loop
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};
return {
activitiesState,
getRecentActivities
};
};
组件
import { useActivities } from '../../services/ActivitiesProvider';
export default function Activity() {
const { activitiesService, activitiesStore } = useActivities();
const { activitiesState, getRecentActivities } = activitiesStore;
const { activities, searchResults, isSearching } = activitiesState;
const [currentActivity, setCurrentActivity] = useState<IClassificationActivity>(null);
const [currentActivities, setCurrentActivities] = useState<IClassificationActivity[]>(null);
//caching the initial load of activities, so infinite loops don't occur if we getRecentActivities later
const storeActivities = useRef<IActivity[]>(isSearching ? searchResults : activities).current;
useEffect(() => {
//Get all the details for selected activity
const getActivityDetails = async () => {
const convertedType: number = type === ActivityType.Website ? 0 : 1;
const convertedId = parseInt(id);
try {
const response = await activityService.getClassificationActivityDetails(
convertedId,
convertedType
);
if (response) {
setActivityDetail(response);
if (storeActivities?.length) {
//if activities are pre-loaded from the Store, but the current activity does not exist in that array, add it to the front of the array and start the prev/next flow from there
const currActivity = storeActivities.filter(
(activity: IActivity) =>
activity.type === type && activity.id === parseInt(id)
)[0];
if (!currActivity) {
const tmpStoreActivities = storeActivities;
tmpStoreActivities.unshift(response);
setCurrentActivity(tmpStoreActivities[0]);
}
}
}
} catch (error) {
notificationService.error('Unable to load this Activity');
} finally {
setIsLoading(false);
}
};
if (id && id !== 'undefined' && id !== 'null' && type) {
getActivityDetails();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id, type, storeActivities, getRecentActivities]);
...
}
ROOT
<ActivitiesProvider>
<Switch>
<Route
path={Routes.Classification(':type', ':id')}
component={Activity}
exact
/>
</Switch>
</ActivitiesProvider>
上下文 API 用于在组件之间共享数据,而无需传递思想道具。这样做是为了避免道具钻井的问题。
上下文 API 所做的只是共享该信息,为了使您的对象不可变,您应该检查 Object.freeze() or some third party libraries like immutable。
当你有一个变量时,在 Javascript 中,当你将它重新分配给另一个变量时,你实际上并没有重新分配它,你只是通过引用指向它。
例如,当你有一个名为 myState
的状态时,它包含一个像这样的对象:
{
foo: "bar"
}
然后你做这样的事情:
export default function App() {
const [myState, setMyState] = useState({ foo: "bar" });
const myVar = myState;
myVar.foo = "foo";
console.log(myState); //Output: {foo: "foo"}
return (
<div className="App">
<div></div>
</div>
);
}
尽管重新分配,控制台日志的输出会告诉您 foo 的值实际上是“foo”。不过别担心,有一种方法可以防止这种情况,它被称为“传播”。
您可以像这样使用展开运算符复制对象:
const myStateCopy = {...myState};
现在,您不再引用 myState
。您可以在此 example. And please refer to the docs 中查看点差的工作原理以获取更多信息
编辑:
您也可以使用数组进行传播。
const myArrayState = [{foo: "bar"}];
const myArrayStateCopy = [...myArrayState];