React UserContext:useEffect 中的条件被忽略
React UserContext: Conditional in useEffect is being ignored
我正在使用 useContext 挂钩制作一个与其他组件共享状态的组件。
现在这个组件也正在将状态保存到本地存储。
var initialState = {
avatar: '/static/uploads/profile-avatars/placeholder.jpg',
isRoutingVisible: false,
removeRoutingMachine: false,
markers: [],
currentMap: {}
};
var UserContext = React.createContext();
function setLocalStorage(key, value) {
function isJson(item) {
item = typeof item !== 'string' ? JSON.stringify(item) : item;
try {
item = JSON.parse(item);
} catch (e) {
return false;
}
if (typeof item === 'object' && item !== null) {
return true;
}
return false;
}
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (errors) {
// catch possible errors:
console.log(errors);
}
}
function getLocalStorage(key, initialValue) {
try {
const value = window.localStorage.getItem(key);
return value ? JSON.parse(value) : initialValue;
} catch (e) {
return initialValue;
}
}
function UserProvider({ children }) {
const [user, setUser] = useState(() => getLocalStorage('user', initialState));
在我声明一些 useEffect 挂钩之后:
const [
isLengthOfUserMarkersLessThanTwo,
setIsLengthOfUserMarkersLessThanTwo
] = useState(true);
useEffect(() => {
setLocalStorage('user', user);
}, [user]);
useEffect(() => {
console.log('user.isRoutingVisibile ', user.isRoutingVisibile);
}, [user.isRoutingVisibile]);
useEffect(() => {
console.log('user.markers.length ', user.markers.length);
if (user.markers.length === 2) {
setIsLengthOfUserMarkersLessThanTwo(false);
}
return () => {};
}, [JSON.stringify(user.markers)]
);
最后一个钩子让人头疼,我在依赖项中传递了一个数组,我想在数组长度达到 2 时做出反应(哈!)做一些事情。
当它到达那里时,我有一个 useState 挂钩,它将更改变量的值。
const [
isLengthOfUserMarkersLessThanTwo,
setIsLengthOfUserMarkersLessThanTwo
] = useState(true);
我有一个函数,我想传递给另一个组件,它应该只能在三元 return 为真时触发。
现在尽管 Array 的长度变为 2,setIsLengthOfUserMarkersLessThanTwo
不会将变量更改为 false
return (
<UserContext.Provider
value={{
setUserMarkers: marker => {
console.log('marker ', marker);
console.log(
'isLengthOfUserMarkersLessThanTwo ',
isLengthOfUserMarkersLessThanTwo
);
isLengthOfUserMarkersLessThanTwo === true
? setUser(user => ({
...user,
markers: [...user.markers, marker]
}))
: () => null;
},
}}
>
{children}
</UserContext.Provider>
);
提前致谢!
感谢 Amirhossein Ebrahimi!他向我指出了 Kent C. Dodds 的 great article!
我基于它添加了这段代码。
我刚刚添加了 reducer,但真的应该查看这篇文章,因为他为上下文添加了一些错误检查(我应该做的事情)。
刚刚添加了这个减速器:
function reducer(state, action) {
switch (action.type) {
case 'isLengthOfMarkersLessThanTwoFalse': {
return {
isLengthOfMarkersLessThanTwo: (state.isLengthOfMarkersLessThanTwo = false)
};
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
const [state, dispatch] = useReducer(reducer, { isLengthOfMarkersLessThanTwo: true });
然后在我的 useEffect 钩子中:
useEffect(() => {
if (user.markers.length === 2) {
dispatch({ type: 'isLengthOfMarkersLessThanTwoFalse' });
}
}, [JSON.stringify(user.markers)]);
然后 return 它在 Provider 中,因此它可以被它的 children:
使用
return (
<UserContext.Provider
value={{
setUserMarkers: marker => {
state.isLengthOfMarkersLessThanTwo // using the var to control the ternary depending on useEffect!
? setUser(user => ({
...user,
markers: [...user.markers, marker]
}))
: () => null;
}}
>
{children}
</UserContext.Provider>
);
再次感谢 Amirhossein!
将函数作为状态传递可能不是你想要的,也传递函数来更新反应状态的一部分,这很容易被一个伟大的舞台管理器解决,比如 redux。对于简单的项目,redux 可能有点矫枉过正,react 本身可以通过 Context 在后台处理这些场景。它也将通过 React Hooks 变得更具可读性。这里是一个简单的演示,展示了如何使用 React hooks 使其更具可预测性。
const UserStateContext = React.createContext()
const UserDispatchContext = React.createContext()
function userReducer(state, {type, payload}){
switch (type) {
case 'setId': {
return {...state, id: payload.id}
}
case 'setAvatar': {
return {...state, avatar: payload.avatar}
}
// ...
default: {
throw new Error(`Unhandled action type: ${type}`)
}
}
}
const initialState = {
avatar: '/static/uploads/profile-avatars/placeholder.jpg',
isRoutingVisible: false,
// ...
};
function UserProvider({children}) {
const [state, dispatch] = React.useReducer(userReducer, initialState)
return (
<UserStateContext.Provider value={state}>
<UserDispatchContext.Provider value={dispatch}>
{children}
</UserDispatchContext.Provider>
</UserStateContext.Provider>
)
}
function useUserState() {
return React.useContext(UserStateContext)
}
function useUserDispatch() {
return React.useContext(CountDispatchContext)
}
function useUser() {
return [useUserState(), useUserDispatch()]
}
现在您可以像下面这样在子组件中使用它:
const AvatarChildren = () => {
const [user, dispatch] = useUser()
return (
<div>
<img src={user.avatar} />
<button
onClick={() => dispatch({ type: 'setAvatar',
payload: {avatar: 'newAvatarSrc'} })}
>
Change Avatar
</button>
</div>
)
}
你甚至可以让它变得更简单,比如
const userReducer = (state, action) => ({state, ...action})
并像这样使用它
onClick={() => dispatch({avatar: 'newAvatarSrc'})}
我正在使用 useContext 挂钩制作一个与其他组件共享状态的组件。
现在这个组件也正在将状态保存到本地存储。
var initialState = {
avatar: '/static/uploads/profile-avatars/placeholder.jpg',
isRoutingVisible: false,
removeRoutingMachine: false,
markers: [],
currentMap: {}
};
var UserContext = React.createContext();
function setLocalStorage(key, value) {
function isJson(item) {
item = typeof item !== 'string' ? JSON.stringify(item) : item;
try {
item = JSON.parse(item);
} catch (e) {
return false;
}
if (typeof item === 'object' && item !== null) {
return true;
}
return false;
}
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (errors) {
// catch possible errors:
console.log(errors);
}
}
function getLocalStorage(key, initialValue) {
try {
const value = window.localStorage.getItem(key);
return value ? JSON.parse(value) : initialValue;
} catch (e) {
return initialValue;
}
}
function UserProvider({ children }) {
const [user, setUser] = useState(() => getLocalStorage('user', initialState));
在我声明一些 useEffect 挂钩之后:
const [
isLengthOfUserMarkersLessThanTwo,
setIsLengthOfUserMarkersLessThanTwo
] = useState(true);
useEffect(() => {
setLocalStorage('user', user);
}, [user]);
useEffect(() => {
console.log('user.isRoutingVisibile ', user.isRoutingVisibile);
}, [user.isRoutingVisibile]);
useEffect(() => {
console.log('user.markers.length ', user.markers.length);
if (user.markers.length === 2) {
setIsLengthOfUserMarkersLessThanTwo(false);
}
return () => {};
}, [JSON.stringify(user.markers)]
);
最后一个钩子让人头疼,我在依赖项中传递了一个数组,我想在数组长度达到 2 时做出反应(哈!)做一些事情。
当它到达那里时,我有一个 useState 挂钩,它将更改变量的值。
const [
isLengthOfUserMarkersLessThanTwo,
setIsLengthOfUserMarkersLessThanTwo
] = useState(true);
我有一个函数,我想传递给另一个组件,它应该只能在三元 return 为真时触发。
现在尽管 Array 的长度变为 2,setIsLengthOfUserMarkersLessThanTwo
不会将变量更改为 false
return (
<UserContext.Provider
value={{
setUserMarkers: marker => {
console.log('marker ', marker);
console.log(
'isLengthOfUserMarkersLessThanTwo ',
isLengthOfUserMarkersLessThanTwo
);
isLengthOfUserMarkersLessThanTwo === true
? setUser(user => ({
...user,
markers: [...user.markers, marker]
}))
: () => null;
},
}}
>
{children}
</UserContext.Provider>
);
提前致谢!
感谢 Amirhossein Ebrahimi!他向我指出了 Kent C. Dodds 的 great article!
我基于它添加了这段代码。
我刚刚添加了 reducer,但真的应该查看这篇文章,因为他为上下文添加了一些错误检查(我应该做的事情)。
刚刚添加了这个减速器:
function reducer(state, action) {
switch (action.type) {
case 'isLengthOfMarkersLessThanTwoFalse': {
return {
isLengthOfMarkersLessThanTwo: (state.isLengthOfMarkersLessThanTwo = false)
};
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
const [state, dispatch] = useReducer(reducer, { isLengthOfMarkersLessThanTwo: true });
然后在我的 useEffect 钩子中:
useEffect(() => {
if (user.markers.length === 2) {
dispatch({ type: 'isLengthOfMarkersLessThanTwoFalse' });
}
}, [JSON.stringify(user.markers)]);
然后 return 它在 Provider 中,因此它可以被它的 children:
使用return (
<UserContext.Provider
value={{
setUserMarkers: marker => {
state.isLengthOfMarkersLessThanTwo // using the var to control the ternary depending on useEffect!
? setUser(user => ({
...user,
markers: [...user.markers, marker]
}))
: () => null;
}}
>
{children}
</UserContext.Provider>
);
再次感谢 Amirhossein!
将函数作为状态传递可能不是你想要的,也传递函数来更新反应状态的一部分,这很容易被一个伟大的舞台管理器解决,比如 redux。对于简单的项目,redux 可能有点矫枉过正,react 本身可以通过 Context 在后台处理这些场景。它也将通过 React Hooks 变得更具可读性。这里是一个简单的演示,展示了如何使用 React hooks 使其更具可预测性。
const UserStateContext = React.createContext()
const UserDispatchContext = React.createContext()
function userReducer(state, {type, payload}){
switch (type) {
case 'setId': {
return {...state, id: payload.id}
}
case 'setAvatar': {
return {...state, avatar: payload.avatar}
}
// ...
default: {
throw new Error(`Unhandled action type: ${type}`)
}
}
}
const initialState = {
avatar: '/static/uploads/profile-avatars/placeholder.jpg',
isRoutingVisible: false,
// ...
};
function UserProvider({children}) {
const [state, dispatch] = React.useReducer(userReducer, initialState)
return (
<UserStateContext.Provider value={state}>
<UserDispatchContext.Provider value={dispatch}>
{children}
</UserDispatchContext.Provider>
</UserStateContext.Provider>
)
}
function useUserState() {
return React.useContext(UserStateContext)
}
function useUserDispatch() {
return React.useContext(CountDispatchContext)
}
function useUser() {
return [useUserState(), useUserDispatch()]
}
现在您可以像下面这样在子组件中使用它:
const AvatarChildren = () => {
const [user, dispatch] = useUser()
return (
<div>
<img src={user.avatar} />
<button
onClick={() => dispatch({ type: 'setAvatar',
payload: {avatar: 'newAvatarSrc'} })}
>
Change Avatar
</button>
</div>
)
}
你甚至可以让它变得更简单,比如
const userReducer = (state, action) => ({state, ...action})
并像这样使用它
onClick={() => dispatch({avatar: 'newAvatarSrc'})}