reactjs - React useReducer 异步数据获取

标签 reactjs react-hooks

我正在尝试使用新的 React useReducer API 获取一些数据,但卡在了我需要异步获取它的阶段。我只是不知道怎么办:/

如何将数据获取放在 switch 语句中,或者这不是应该完成的方法?

import React from 'react'

const ProfileContext = React.createContext()

const initialState = {
  data: false
}

let reducer = async (state, action) => {
  switch (action.type) {
    case 'unload':
      return initialState
    case 'reload':
      return { data: reloadProfile() } //how to do it???
  }
}


const reloadProfile = async () => {
  try {
    let profileData = await fetch('/profile')
    profileData = await profileData.json()

    return profileData
  } catch (error) {
    console.log(error)
  }
}

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState)

  return (
    <ProfileContext.Provider value={{ profile, profileR }}>
      {props.children}
    </ProfileContext.Provider>
  )
}

export { ProfileContext, ProfileContextProvider }

我试图这样做,但它不适用于异步;(

let reducer = async (state, action) => {
  switch (action.type) {
    case 'unload':
      return initialState
    case 'reload': {
      return await { data: 2 }
    }
  }
}

最佳答案

这是一个有趣的案例,useReducer 示例没有涉及。我认为 reducer 不是异步加载的正确位置。从 Redux 的思维方式来看,您通常会在其他地方加载数据,无论是在 thunk 中,还是在 observable 中(例如 redux-observable),或者只是在诸如 componentDidMount 之类的生命周期事件中。借助新的 useReducer,我们可以通过 useEffect 使用 componentDidMount 方法。您的效果可能类似于以下内容:

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState);

  useEffect(() => {
    reloadProfile().then((profileData) => {
      profileR({
        type: "profileReady",
        payload: profileData
      });
    });
  }, []); // The empty array causes this effect to only run on mount

  return (
    <ProfileContext.Provider value={{ profile, profileR }}>
      {props.children}
    </ProfileContext.Provider>
  );
}

此外,这里的工作示例:https://codesandbox.io/s/r4ml2x864m .

如果您需要将 prop 或状态传递给 reloadProfile 函数,您可以通过将第二个参数调整为 useEffect (示例中的空数组)来实现)以便它仅在需要时运行。您需要检查以前的值或实现某种缓存以避免在不必要时进行提取。

更新 - 从子级重新加载

如果您希望能够从子组件重新加载,有几种方法可以实现。第一个选项是将回调传递给将触发调度的子组件。这可以通过上下文提供者或组件 Prop 来完成。由于您已经在使用上下文提供程序,因此以下是该方法的示例:

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState);

  const onReloadNeeded = useCallback(async () => {
    const profileData = await reloadProfile();
    profileR({
      type: "profileReady",
      payload: profileData
    });
  }, []); // The empty array causes this callback to only be created once per component instance

  useEffect(() => {
    onReloadNeeded();
  }, []); // The empty array causes this effect to only run on mount

  return (
    <ProfileContext.Provider value={{ onReloadNeeded, profile }}>
      {props.children}
    </ProfileContext.Provider>
  );
}

如果您确实想要使用调度函数而不是显式回调,则可以通过将调度包装在一个高阶函数中来实现,该函数处理本来由中间件处理的特殊操作在 Redux 世界中。这是一个例子。请注意,我们不是将 profileR 直接传递到上下文提供程序中,而是传递充当中间件的自定义提供程序,拦截 reducer 不关心的特殊操作。

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState);

  const customDispatch= useCallback(async (action) => {
    switch (action.type) {
      case "reload": {
        const profileData = await reloadProfile();
        profileR({
          type: "profileReady",
          payload: profileData
        });
        break;
      }
      default:
        // Not a special case, dispatch the action
        profileR(action);
    }
  }, []); // The empty array causes this callback to only be created once per component instance

  return (
    <ProfileContext.Provider value={{ profile, profileR: customDispatch }}>
      {props.children}
    </ProfileContext.Provider>
  );
}

关于reactjs - React useReducer 异步数据获取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53146795/

相关文章:

javascript - 使用 styled-components 的 createGlobalStyle() 从文件创建全局样式

javascript - redux-saga 注入(inject)两次

javascript - 如何将函数放在 return 之外,然后从 onChange react 中调用它?

javascript - 使用自定义 React hook 获取数据

javascript - 如何在同一个按钮上使用 react router 和 react scroll?

reactjs - 通过 React Hooks 在 React 上使用动态导入视频

javascript - Gatsby 混合应用程序 - 使用 `createPage` 中的模板构建的页面上的子路由问题

javascript - 未处理的 promise 拒绝 [错误 : Got an invalid value for 'component' prop for the screen 'LocationType' . 它必须是有效的 React 组件。]

ruby-on-rails - 为什么我的 axios get 调用一遍又一遍地重复使用 React.useEffect 从 Rails 后端获取?

javascript - 如何防止重新渲染未更改的组件?