Reactjs React useReducer异步数据获取

Reactjs React useReducer异步数据获取,reactjs,react-hooks,Reactjs,React Hooks,我正试图用新的react useReducer API获取一些数据,但我仍停留在需要异步获取数据的阶段。我只是不知道如何:/ 如何将数据获取放在switch语句中,或者这不是一种应该怎么做的方法 import React from 'react' const ProfileContext = React.createContext() const initialState = { data: false } let reducer = async (state, action) =&g

我正试图用新的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 }

这是一个有趣的案例,
useReducer
示例没有涉及到。我认为减速器不是异步加载的正确位置。出于Redux思维,您通常会在其他地方加载数据,要么是在thunk中,要么是在observable(例如Redux observable)中,要么就是在生命周期事件中,比如
componentDidMount
。使用新的
useReducer
我们可以使用
useffect
使用
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>
  );
}
如果您真的想使用分派函数而不是显式回调,那么可以通过将分派包装在一个更高阶的函数中来实现,该函数将处理Redux世界中中间件将要处理的特殊操作。这里有一个例子。请注意,我们没有将
探查器
直接传递到上下文提供程序中,而是传递一个像中间件一样的自定义提供程序,截取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>
  );
}
函数配置文件ContextProvider(props){
let[profile,profileR]=React.useReducer(reducer,initialState);
const customDispatch=useCallback(异步(操作)=>{
开关(动作类型){
案例“重新加载”:{
const profileData=await reloadProfile();
剖析器({
键入:“profileReady”,
有效载荷:配置文件数据
});
打破
}
违约:
//不是特例,快行动吧
档案员(行动);
}
},[]);//空数组导致每个组件实例只创建一次回调
返回(
{props.children}
);
}

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

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


我用一个层包装了分派方法,以解决异步操作问题

这是初始状态。
加载
键记录应用程序当前的加载状态,当应用程序从服务器获取数据时,当您想要显示加载页面时,这很方便

{
值:0,
加载:错误
}
有四种行为

功能减速机(状态、动作){
开关(动作类型){
案例“单击异步”:
案例“单击同步”:
返回{…状态,值:action.payload};
案例“加载\启动”:
返回{…状态,加载:true};
案例“装载结束”:
返回{…状态,加载:false};
违约:
抛出新错误();
}
}
功能isPromise(obj){
返回(
!!obj&&
(对象类型==“对象”|对象类型==“函数”)&&
对象的类型。然后==“函数”
);
}
函数包装器分派(分派){
返回函数(动作){
如果(isPromise(action.payload)){
分派({type:“加载启动”});
action.payload.then(v=>{
分派({type:action.type,有效负载:v});
分派({type:“loading_end”});
});
}否则{
派遣(行动);
}
};
}
假设有一个异步方法

异步函数asyncFetch(p){ 返回新承诺(解决=>{ 设置超时(()=>{ 决心(p); }, 1000); }); }
wrapper分派(分派)({
键入:“单击异步”,
有效负载:asyncFetch(新日期().getTime())
});
完整的示例代码如下所示:

这是一个很好的实践。它将使
useReducer
更加可预测,并简化可测试性。随后的两种方法都将异步操作与纯还原器结合起来:

1.在调度之前获取数据(简单) 使用
asyncDispatch
包装原始
dispatch
,并让上下文向下传递此函数:

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-AppContextProvider=({children})=>{
const[state,dispatch]=useReducer(reducer,initState);
const asyncDispatch=()=>{//根据需要调整参数
分派({type:“装入”});
fetchData()。然后(数据=>{
分派({type:“finished”,payload:data});
});
};
返回(
{儿童}
);
//注意:如果提供程序更频繁地重新呈现,请记住上下文值
};
const reducer=(状态,{type,payload})=>{
if(type==“loading”)返回{status:“loading”};
if(type==“finished”)返回{状态:“finished”,数据:payload};
返回状态;
};
常量initState={
状态:“空闲”
};
const AppContext=React.createContext();
const AppContextProvider=({children})=>{
const[state,dispatch]=React.useReducer(reducer,initState);
const asyncDispatch=()=>{//根据需要调整参数
分派({type:“装入”});
fetchData()。然后(数据=>{
分派({type:“finished”,payload:data});
});
};
返回(
{儿童}
);
};
函数App(){
返回(
);
}
常量Child=()=>{
const val=React.useContext(AppContext);
常数{
状态:{状态,数据},
派遣
}=val;
返回(
状态:{Status}

数据:{Data | |“-”}

获取数据 ); }; 函数fetchData(){ 返回新承诺(解决=>{ 设置超时(()=>{ 决心(42); }, 2000); }); } render(,document.getElementById(“根”))
您可以使用UseAncy包:,它基本上是
useReducer
钩子的扩展,允许通过http请求管理应用程序状态上的异步操作

通过
设置客户端代理(您可以使用自己的http客户端)
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>
  );
}
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
};
function useAsyncReducer(reducer, initState) {
    const [state, setState] = useState(initState),
        dispatchState = async (action) => setState(await reducer(state, action));
    return [state, dispatchState];
}

async function reducer(state, action) {
    switch (action.type) {
        case 'switch1':
            // Do async code here
            return 'newState';
    }
}

function App() {
    const [state, dispatchState] = useAsyncReducer(reducer, 'initState');
    return <ExampleComponent dispatchState={dispatchState} />;
}

function ExampleComponent({ dispatchState }) {
    return <button onClick={() => dispatchState({ type: 'switch1' })}>button</button>;
}
async function updateFunction(action) {
    switch (action.type) {
        case 'switch1':
            // Do async code here (access current state with 'action.state')
            action.setState('newState');
            break;
    }
}

function App() {
    const [state, setState] = useState(),
        callUpdateFunction = (vars) => updateFunction({ ...vars, state, setState });

    return <ExampleComponent callUpdateFunction={callUpdateFunction} />;
}

function ExampleComponent({ callUpdateFunction }) {
    return <button onClick={() => callUpdateFunction({ type: 'switch1' })} />
}
useEffect(() => {
if (resultFetch) {
  const user = resultFetch;
  dispatch({ type: AC_USER_LOGIN, userId: user.ID})

}}, [resultFetch])