当前位置: 首页 > 知识库问答 >
问题:

React useReducer异步数据获取

罗渝
2023-03-14

我正在尝试使用新的reactuseReucker 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 }
    }
  }
}

共有3个答案

费子濯
2023-03-14

我写了一篇非常详细的问题解释和可能的解决方案。丹·阿布拉莫夫提出了解决方案3。

注意:gist中的示例提供了文件操作的示例,但对于数据获取也可以采用相同的方法。

https://gist.github.com/astoilkov/013c513e33fe95fa8846348038d8fe42

壤驷心思
2023-03-14

保持减速器的纯净是一种很好的做法。它将使useReducer更具可预测性并简化可测试性。后续方法都将异步操作与纯减速器相结合:

用asyncDispatch(异步调度)包装原始调度,并让上下文传递此函数:

const AppContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initState);
  const asyncDispatch = () => { // adjust args to your needs
    dispatch({ type: "loading" });
    fetchData().then(data => {
      dispatch({ type: "finished", payload: data });
    });
  };
  
  return (
    <AppContext.Provider value={{ state, dispatch: asyncDispatch }}>
      {children}
    </AppContext.Provider>
  );
  // Note: memoize the context value, if Provider gets re-rendered more often
};
const reducer = (state, { type, payload }) => {
  if (type === "loading") return { status: "loading" };
  if (type === "finished") return { status: "finished", data: payload };
  return state;
};

const initState = {
  status: "idle"
};

const AppContext = React.createContext();

const AppContextProvider = ({ children }) => {
  const [state, dispatch] = React.useReducer(reducer, initState);
  const asyncDispatch = () => { // adjust args to your needs
    dispatch({ type: "loading" });
    fetchData().then(data => {
      dispatch({ type: "finished", payload: data });
    });
  };

  return (
    <AppContext.Provider value={{ state, dispatch: asyncDispatch }}>
      {children}
    </AppContext.Provider>
  );
};

function App() {
  return (
    <AppContextProvider>
      <Child />
    </AppContextProvider>
  );
}

const Child = () => {
  const val = React.useContext(AppContext);
  const {
    state: { status, data },
    dispatch
  } = val;
  return (
    <div>
      <p>Status: {status}</p>
      <p>Data: {data || "-"}</p>
      <button onClick={dispatch}>Fetch data</button>
    </div>
  );
};

function fetchData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(42);
    }, 2000);
  });
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
轩辕奕
2023-03-14

这是一个有趣的案例,useReducer示例没有涉及到。我认为减速器不是异步加载的合适位置。从Redux的思维模式来看,您通常会在其他地方加载数据,要么在thunk中,要么在可观察的(例如。redux-可观察的)中,要么只是在生命周期事件中,例如组件ddMount。使用新的useReduer,我们可以使用use效果使用组件ddMount方法。您的效果可以如下所示:

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.

如果需要将道具或状态传递给函数reloadProfile,可以将第二个参数调整为useffect(示例中的空数组),使其仅在需要时运行。您需要对照前面的值进行检查,或者实现某种缓存,以避免在不必要时进行提取。

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

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世界中中间件处理的特殊操作。这是一个例子。请注意,我们不是将filileR直接传递给上下文提供程序,而是传递充当中间件的自定义提供程序,拦截还原器不关心的特殊操作。

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>
  );
}
 类似资料:
  • 问题内容: 我正在尝试使用新的react useReducer API来获取一些数据,并停留在需要异步获取的阶段。我只是不知道如何:/ 如何将数据获取放置在switch语句中,或者这不是应该完成的方式? 我试图这样做,但它不能与异步一起工作;( 问题答案: 这是一个有趣的案例,示例没有涉及。我认为减速器不是异步加载的正确位置。来自Redux的心态,您通常会将数据加载到其他位置,例如以thunk,可

  • Nuxt.js 扩展了 Vue.js,增加了一个叫 asyncData 的方法,使得我们可以在设置组件的数据之前能异步获取或处理数据。 使用Nuxt.js的异步数据 了解如何使用Nuxt.js管理异步数据。 由VueSchool制作视频课程,用于支持Nuxt.js开发。 asyncData 方法 asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。

  • 默认情况下,createStore() 所创建的 Redux store 没有使用 middleware,所以只支持 同步数据流。 你可以使用 applyMiddleware() 来增强 createStore()。虽然这不是必须的,但是它可以帮助你用简便的方式来描述异步的 action。 像 redux-thunk 或 redux-promise 这样支持异步的 middleware 都包装了

  • Nuxt.js 扩展了 Vue.js,增加了一个叫asyncData的方法,使得我们可以在设置组件的数据之前能异步获取或处理数据。 asyncData 方法 asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。 在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用asyncData方法来获取数据,Nuxt.js 会将async

  • 本文向大家介绍jQuery异步获取json数据方法汇总,包括了jQuery异步获取json数据方法汇总的使用技巧和注意事项,需要的朋友参考一下 jQuery异步获取json数据有2种方式,一个是$.getJSON方法,一个是$.ajax方法。本篇体验使用这2种方式异步获取json数据,然后追加到页面。 在根目录下创建data.json文件: ■ 通过$.getJSON方法获取json数据   ■

  • 参数: url - 返回JSON格式数据,数据格式与添加(更新)数据定义的data相同 asyncLoadCallback - 当加载完成时执行的回调函数 从一个数据源异步加载数据。用法: var url = "sampleData.json"; // 使用addDataAsync() API异步加载URL中的数据。 // 回调将在完成数据加载后执行。 controller.addDataAsyn