Reactjs React useReducer异步数据获取
我正试图用新的react useReducer API获取一些数据,但我仍停留在需要异步获取数据的阶段。我只是不知道如何:/ 如何将数据获取放在switch语句中,或者这不是一种应该怎么做的方法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
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])