我如何将参数传递给 usePromise React 钩子中的承诺
How can I pass a param to a promise inside usePromise React hook
我创建了一个 usePromise
React Hook,它应该能够解析每一种 javascript promise 和 returns 每个结果和状态:数据、解析状态和错误。
我能够让它在没有任何参数的情况下传递函数,但是当我尝试更改它以允许参数时,我得到一个无限循环。
const usePromise = (promise: any): [any, boolean, any] => {
const [data, setData] = useState<object | null>(null);
const [error, setError] = useState<object | null>(null);
const [fetching, setFetchingState] = useState<boolean>(true);
useEffect(() => {
setFetchingState(true);
promise
.then((data: object) => {
setData(data);
})
.catch((error: object) => {
setError(error);
})
.finally(() => {
setFetchingState(false);
});
}, [promise]);
return [data, fetching, error];
};
const apiCall = (param?: string): Promise<any> => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ response: `Response generated with your param ${param}.` });
}, 500);
});
};
const App = (): React.Element => {
// How can I pass an argument to apiCall?
const [response, fetching, error] = usePromise(apiCall(5));
console.log("render"); // This logs infinitely
return <div>{JSON.stringify({ response, fetching, error })}</div>;
};
您可以在 https://codesandbox.io/s/react-typescript-fl13w
查看 工作代码 (不带参数)
错误 位于(选项卡卡住,建议):https://codesandbox.io/s/react-typescript-9ow82
注意:我想在不使用 NPM 或类似的 usePromise 单函数库的情况下找到解决方案
自定义挂钩可能会执行多次。你应该这样设计,你想做的所有事情(例如 API 调用)都在 useEffect
钩子中。这可以通过在挂钩中调用回调来实现。
此外,类型安全性更高:
const usePromise = <T>(task: () => Promise<T>) => {
const [state, setState] = useState<[T?, boolean, Error?]>([null, true, null]);
useEffect(() => {
task()
.then(result => setState([result, false, null])
.catch(error => setState([null, false, error]);
}, []); // << omit the condition here, functions don't equal each other²
return state;
};
// Then used as
usePromise(() => apiCall(5));
² 是的,这通常是一种不好的做法,但由于 task
不应该在这里改变,我认为这很好
根据要求,这是我在我的一些项目中使用的版本:
export function useAPI<Q, R>(api: (query: Q) => Promise<R | never>) {
const [state, setState] = useState<{ loading?: true, pending?: true, error?: string, errorCode?: number, result?: R }>({ pending: true });
async function run(query: Q) {
if(state.loading) return;
setState({ loading: true });
try {
const result = await api(query);
setState({ result });
} catch(error) {
if(error instanceof HTTPError) {
console.error(`API Error: ${error.path}`, error);
setState({ error: error.message, errorCode: error.code });
} else {
setState({ error: error.message, errorCode: NaN });
}
}
}
function reset() {
setState({ pending: true });
}
return [state, run, reset] as const;
}
我创建了一个 usePromise
React Hook,它应该能够解析每一种 javascript promise 和 returns 每个结果和状态:数据、解析状态和错误。
我能够让它在没有任何参数的情况下传递函数,但是当我尝试更改它以允许参数时,我得到一个无限循环。
const usePromise = (promise: any): [any, boolean, any] => {
const [data, setData] = useState<object | null>(null);
const [error, setError] = useState<object | null>(null);
const [fetching, setFetchingState] = useState<boolean>(true);
useEffect(() => {
setFetchingState(true);
promise
.then((data: object) => {
setData(data);
})
.catch((error: object) => {
setError(error);
})
.finally(() => {
setFetchingState(false);
});
}, [promise]);
return [data, fetching, error];
};
const apiCall = (param?: string): Promise<any> => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ response: `Response generated with your param ${param}.` });
}, 500);
});
};
const App = (): React.Element => {
// How can I pass an argument to apiCall?
const [response, fetching, error] = usePromise(apiCall(5));
console.log("render"); // This logs infinitely
return <div>{JSON.stringify({ response, fetching, error })}</div>;
};
您可以在 https://codesandbox.io/s/react-typescript-fl13w
查看 工作代码 (不带参数)错误 位于(选项卡卡住,建议):https://codesandbox.io/s/react-typescript-9ow82
注意:我想在不使用 NPM 或类似的 usePromise 单函数库的情况下找到解决方案
自定义挂钩可能会执行多次。你应该这样设计,你想做的所有事情(例如 API 调用)都在 useEffect
钩子中。这可以通过在挂钩中调用回调来实现。
此外,类型安全性更高:
const usePromise = <T>(task: () => Promise<T>) => {
const [state, setState] = useState<[T?, boolean, Error?]>([null, true, null]);
useEffect(() => {
task()
.then(result => setState([result, false, null])
.catch(error => setState([null, false, error]);
}, []); // << omit the condition here, functions don't equal each other²
return state;
};
// Then used as
usePromise(() => apiCall(5));
² 是的,这通常是一种不好的做法,但由于 task
不应该在这里改变,我认为这很好
根据要求,这是我在我的一些项目中使用的版本:
export function useAPI<Q, R>(api: (query: Q) => Promise<R | never>) {
const [state, setState] = useState<{ loading?: true, pending?: true, error?: string, errorCode?: number, result?: R }>({ pending: true });
async function run(query: Q) {
if(state.loading) return;
setState({ loading: true });
try {
const result = await api(query);
setState({ result });
} catch(error) {
if(error instanceof HTTPError) {
console.error(`API Error: ${error.path}`, error);
setState({ error: error.message, errorCode: error.code });
} else {
setState({ error: error.message, errorCode: NaN });
}
}
}
function reset() {
setState({ pending: true });
}
return [state, run, reset] as const;
}