【发布时间】:2022-01-06 00:11:39
【问题描述】:
我正在学习关于 Udemy 的 React 课程。在这个模块中,我们有一个简单的任务应用程序来演示自定义钩子。我遇到过在 App.js 文件中管理“任务”状态的情况,“useHttp”自定义钩子有一个函数“fetchTasks”,当在 App.js 中调用时,它接受“transformTasks”作为参数。我遇到的问题是“transformTasks”操纵了 App.js 中的“任务”状态,但它实际上是在“useHttp”自定义钩子中被调用和执行的。真的很想帮助理解它是如何工作的机制。在没有传入状态的情况下从另一个文件调用时如何操作状态?该代码确实按预期工作。这是完整应用程序的 github 链接,以下是两个相关文件:https://github.com/yanichik/react-course/tree/main/full-course/custom-hooks-v2
这里是 App.js 文件:
import React, { useEffect, useMemo, useState } from "react";
import Tasks from "./components/Tasks/Tasks";
import NewTask from "./components/NewTask/NewTask";
import useHttp from "./custom-hooks/useHttp";
function App() {
// manage tasks state here at top level
const [tasks, setTasks] = useState([]);
const myUrl = useMemo(() => {
return {
url: "https://react-http-104c4-default-rtdb.firebaseio.com/tasks.json",
};
}, []);
const { isLoading, error, sendRequest: fetchTasks } = useHttp();
useEffect(() => {
// func transforms loaded data to add id (firebase-generated), push to loadedTasks, then
// push to tasks state
const transformTasks = (taskObj) => {
let loadedTasks = [];
for (const taskKey in taskObj) {
loadedTasks.push({ id: taskKey, text: taskObj[taskKey].text });
}
setTasks(loadedTasks);
};
fetchTasks(myUrl, transformTasks);
// if you add fetchTasks as a dependency this will trigger a re-render each time states
// are set inside sendRequest (ie fetchTasks) and with each render the custom hook (useHttp)
// will be recalled to continue the cycle. to avoid this, wrap sendRequest with useCallback
}, [fetchTasks, myUrl]);
const addTaskHandler = (task) => {
setTasks((prevTasks) => prevTasks.concat(task));
};
return (
<React.Fragment>
<NewTask onEnterTask={addTaskHandler} />
<Tasks
items={tasks}
loading={isLoading}
error={error}
onFetch={fetchTasks}
/>
</React.Fragment>
);
}
export default App;
这里是“useHttp”自定义钩子:
import { useState, useCallback } from "react";
// NOTE that useCallback CANNOT be used on the top level function
function useHttp() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const sendRequest = useCallback(async (httpConfig, applyFunction) => {
setIsLoading(true);
setError(false);
try {
const response = await fetch(httpConfig.url, {
method: httpConfig.method ? httpConfig.method : "GET",
headers: httpConfig.headers ? httpConfig.headers : {},
body: httpConfig.body ? JSON.stringify(httpConfig.body) : null,
});
// console.log("response: " + response.method);
if (!response.ok) {
throw new Error("Request failed!");
}
const data = await response.json();
applyFunction(data);
// console.log("the formatted task is:" + applyFunction(data));
} catch (err) {
setError(err.message || "Something went wrong!");
}
setIsLoading(false);
}, []);
return { sendRequest, isLoading, error };
}
export default useHttp;
【问题讨论】:
标签: javascript reactjs react-hooks