在这里反应菜鸟。我正在尝试制作一个简单的练习应用程序来帮助我进行React学习,并且出现了很多奇怪的行为。这是一个Todo应用,具有多个待办事项列表供您选择。我要执行的操作是待办事项列表的列表,您可以在其中选择一个和待办事项(例如wunderlist / msft待办事项)。选择一个不同的列表,它显示待办事项,等等。这时,它使用静态json,其中每个项目都有一个子数组。
useEffect-我正在尝试使用它来加载数据。它一直抱怨缺少依赖项。当我添加他们时,它也会抱怨。它抱怨我是否使用空数组作为第二个参数,并且似乎多次触发。
useState-我正在使用它来存储数据。它被初始化为一个空数组。但是render在useEffect之前触发,这是因为我的数据是未定义的,所以我的第二个列表从不渲染。
我在代码中有几个console.log,它们都多次触发。
我敢肯定这只是菜鸟的错误,但是我现在很困惑。这是我的代码:
data / Todo.js
const TodoData = [
{
Id: 1,
Title: "Groceries",
TodoList: [
{
Id: 1,
Title: "Apples"
},
{
Id: 2,
Title: "Oranges"
},
{
Id: 3,
Title: "Bananas"
}
]
},
{
Id: 2,
Title: "Daily Tasks",
TodoList: [
{
Id: 11,
Title: "Clean Kitchen"
},
{
Id: 12,
Title: "Feed Pets"
},
{
Id: 13,
Title: "Do Stuff"
}
]
},
{
Id: 3,
Title: "Hardware Store",
TodoList: []
},
{
Id: 4,
Title: "Costco",
TodoList: [
{
Id: 21,
Title: "Diapers"
},
{
Id: 22,
Title: "Cat Food"
},
{
Id: 23,
Title: "Apples"
},
{
Id: 24,
Title: "Bananas"
}
]
},
{
Id: 5,
Title: "Work",
TodoList: [
{
Id: 34,
Title: "TPS Reports"
}
]
}
];
export default TodoData;
App.Js
import React, { useEffect, useState } from "react";
import TodoData from "./data/Todo.js";
function App() {
const [todoData, setTodoData] = useState([]);
const [todoDetails, setTodoDetails] = useState([]);
useEffect(() => {
if (todoData.length === 0) {
getTodoData();
}
}, [todoData]);
const getTodoData = () => {
setTodoData(TodoData);
console.log("getting todo data");
getTodoDetails(1);
};
const getTodoDetails = id => {
const result = TodoData.filter(x => x.Id === id);
console.log(result[0]);
setTodoDetails(result[0]);
};
const handleClick = e => {
const selectedId = Number(e.target.getAttribute("data-id"));
getTodoDetails(selectedId);
};
return (
<div className="App">
<div className="container-fluid">
<div className="row">
<div className="list-group col-md-4 offset-md-1">
{todoData.map(todos => (
<button
key={todos.Id}
data-id={todos.Id}
className="btn list-group-item d-flex justify-content-between align-items-center"
onClick={handleClick}
>
{todos.Title}
<span className="badge badge-primary badge-pill">
{todos.TodoList.length}
</span>
</button>
))}
</div>
<div className="col-md-6">
<ul className="list-group">
<h2>{todoDetails.Title} List</h2>
{console.log(todoDetails.TodoList)}
{/* this fails miserably */}
{/* {todoDetails.TodoList.map(details => (
<li className="list-group-item" key={details.Id}>
{details.Title}
</li>
))} */}
<li className="list-group-item">Cras justo odio</li>
<li className="list-group-item">Dapibus ac facilisis in</li>
<li className="list-group-item">Morbi leo risus</li>
</ul>
</div>
</div>
</div>
</div>
);
}
export default App;
答案 0 :(得分:4)
Q: useEffect-我正在尝试使用它来加载数据。它一直抱怨缺少依赖项
A:,您可以通过eslint (react-hooks/exhaustive-deps)
忽略它,没有什么可担心的
Q: useState-我正在使用它来存储数据。它被初始化为一个空数组。但是render在useEffect之前触发,这是因为我的数据是未定义的,所以我的第二个列表从不渲染。
A:请阅读代码中的注释,希望能消除您的疑问
// you are intializing `todoDetails` with array, when there will be object
// hence the error while mapping inside the html/jsx
// const [todoDetails, setTodoDetails] = useState([]);
// init with `null`, why?
const [todoDetails, setTodoDetails] = useState(null);
// so that you can do something like this
{
todoDetails && ( // <---------- HERE
<ul className="list-group">
<h2>{todoDetails.Title} List</h2>
{console.log(todoDetails.TodoList)}
{/* this fails miserably */}
{todoDetails.TodoList.map(details => (
<li className="list-group-item" key={details.Id}>
{details.Title}
</li>
))}
</ul>
)
}
Q:现在您将获得多个console.log
,
A:原因是<React.StrictMode>
,
React可以在提交之前多次调用渲染阶段生命周期,也可以根本不提交而调用它们(由于错误或更高优先级的中断)。
我已在演示中将其从index.js
中删除,因此您可以看到差异
工作演示:
答案 1 :(得分:2)
您注释并说失败的代码是由于todoDetails.TodoList
在您首次渲染组件时未定义。为避免这种情况,请用一个项目初始化todoDetails
或在调用todoDetails.TodoList
之前检查是否定义了.map
。
{todoDetails.TodoList ? todoDetails.TodoList.map(details => (
<li className="list-group-item" key={details.Id}>
{details.Title}
</li>
)) : null}
或
const [todoDetails, setTodoDetails] = useState(TodoData[0]);
不必太担心eslint (react-hooks/exhaustive-deps)
错误。您需要了解useEffect
的第二个参数的含义。在您的代码中,您将todoData
传递给了useEffect
:
useEffect(() => {
if (todoData.length === 0) {
getTodoData();
}
}, [todoData]);
这意味着,每当todoData
发生变化时,useEffect
就会像componentDidUpdate
一样运行。如果仅传递一个空数组,则意味着您的useEffect没有依赖项,并且将仅执行一次,其行为类似于componentDidMount
。所以,您在这里做的很好。
除此之外,以下是一些我可以为您提供的代码的提示:
关于您的handleClick
函数,您可以删除它,并简单地调用getTodoDetails(id)
传递ID,而不是将其添加到data-id
并在以后通过以下方式获取它:
<button
key={todos.Id}
className="btn list-group-item d-flex justify-content-between align-items-center"
onClick={() => getTodoDetails(todos.Id)} // <---- change is in here
>
{todos.Title}
<span className="badge badge-primary badge-pill">
{todos.TodoList.length}
</span>
</button>
在您的getTodoDetails
上,您可以将filter
更改为find
,因为您不需要遍历整个数组来查找一项,因此价格更高。另外,这样,您就不需要访问数组的第一项。
const getTodoDetails = id => {
const result = TodoData.find(x => x.Id === id);
console.log(result);
setTodoDetails(result);
};
您将todoDetails
用作object
,并将其初始化为array
。如果您的todoDetails
总是只有一个待办事项,请将其初始化为一个对象,这样您就可以防止从数组访问todoDetails.Title
。 useEffect
将始终在渲染后执行。
const [todoDetails, setTodoDetails] = useState({});
答案 2 :(得分:1)
您必须将getTodoData的逻辑放入useEffect
钩子中
useEffect(() => {
const getTodoData = () => {
setTodoData(TodoData);
console.log("getting todo data");
getTodoDetails(1);
};
if (todoData.length === 0) {
getTodoData();
}
}, [todoData.length]);
渲染中
{
// check todoDetails has TodoList
todoDetails.TodoList &&
todoDetails.TodoList.map((details) => (
<li className="list-group-item" key={details.Id}>
{details.Title}
</li>
));
}
这是更新后的demo
答案 3 :(得分:1)
useEffect
挂钩有时可能很奇怪。要解决此问题,您只需像下面这样直接传递getTodoData
函数:
const getTodoData = () => {
setTodoData(TodoData);
console.log("getting todo data");
getTodoDetails(1);
};
useEffect(getTodoData, []);
在将函数getTodoData
传递到useEffect
之前定义App.js
函数很重要,否则会出错。
我对您的“惨败”的代码未加注释,并且能够使所有内容正常运行。
这是您的固定import React, { useEffect, useState } from "react";
import TodoData from "./data/Todo.js";
function App() {
const [todoData, setTodoData] = useState([]);
const [todoDetails, setTodoDetails] = useState([]);
const getTodoData = () => {
setTodoData(TodoData);
console.log("getting todo data");
getTodoDetails(1);
};
useEffect(getTodoData, []);
const getTodoDetails = id => {
const result = TodoData.filter(x => x.Id === id);
console.log(result[0]);
setTodoDetails(result[0]);
};
const handleClick = e => {
const selectedId = Number(e.target.getAttribute("data-id"));
getTodoDetails(selectedId);
};
return (
<div className="App">
<div className="container-fluid">
<div className="row">
<div className="list-group col-md-4 offset-md-1">
{todoData.map(todos => (
<button
key={todos.Id}
data-id={todos.Id}
className="btn list-group-item d-flex justify-content-between align-items-center"
onClick={handleClick}
>
{todos.Title}
<span className="badge badge-primary badge-pill">
{todos.TodoList.length}
</span>
</button>
))}
</div>
<div className="col-md-6">
<ul className="list-group">
<h2>{todoDetails.Title} List</h2>
{console.log(todoDetails.TodoList)}
{todoDetails.TodoList.map(details => (
<li className="list-group-item" key={details.Id}>
{details.Title}
</li>
))}
</ul>
</div>
</div>
</div>
</div>
);
}
export default App;
固定文件的全部:
useEffect
This Stackoverflow post与您的相似,可能会帮助您了解将来在使用 position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
钩子时如何避免错误。我希望这会有所帮助!