【问题标题】:React UserContext: Conditional in useEffect is being ignoredReact UserContext:useEffect中的条件被忽略
【发布时间】:2021-01-04 17:08:50
【问题描述】:

我正在使用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);

我有一个函数,我想将它传递给另一个组件,该组件只能在三元返回 true 时触发。

现在,尽管数组的长度变为 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>
  );

提前谢谢你!

【问题讨论】:

  • 你能把它组合到一个codeandbox中吗?
  • 最后一个useEffect也不需要返回noop函数
  • @AmirhosseinEbrahimi 这个应用程序使用了各种 API,所以它们无法工作,LMK 如果这个 link 工作。
  • 它由于它的大小无法运行,但是我看到了你的源代码,并且你需要 useReducer,你可以看看这个更大的article。如果你愿意,我可以写一个关于如何使用 useReducer 进行转换的答案
  • 我真的想通了我的朋友! Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime!新年快乐!

标签: reactjs react-hooks use-context


【解决方案1】:

将函数作为状态传递可能不是你想要的,同时传递函数来更新一片反应状态,这只是通过 redux 等出色的舞台管理器来解决。对于简单的项目,redux 可能有点矫枉过正,react 本身可以通过 Context 后台处理这些场景。使用 React Hooks 也将变得更具可读性。这是一个简单的演示,展示了如何使用 React 钩子使其更可预测。

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'})}

【讨论】:

    【解决方案2】:

    感谢 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)]);
    

    然后在 Provider 中返回它,以便它的孩子可以使用它:

    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>
      );
    

    再次感谢阿米尔侯赛因!

    【讨论】:

      猜你喜欢
      • 2020-12-14
      • 2021-01-22
      • 2018-07-13
      • 2019-06-02
      • 2016-02-09
      • 2021-08-04
      • 2019-09-11
      • 1970-01-01
      • 2022-12-09
      相关资源
      最近更新 更多