不使用 useEffect 更新状态落后一步?
Updating state being one step behind WITHOUT using useEffect?
注意:我看到有人建议使用 useEffect 解决这个问题,但我不会在这里通过 useEffect 更新状态..
我遇到的问题是,例如,当用户选择 id 7 时,它会触发 App.tsx 中的一个函数并过滤待办事项列表数据并使用过滤后的列表更新状态。但是在浏览器中,它不会立即反映更新状态。它落后了一步。
这是一个Demo
如何解决这个问题(不合并 App.tsx 和 TodoSelect.tsx)?
function App() {
const [todoData, setTodoData] = useState<Todo[]>([]);
const [filteredTodoList, setFilteredTodoList] = useState<Todo[]>([]);
const [selectedTodoUser, setSelectedTodoUser] = useState<string | null>(null);
const filterTodos = () => {
let filteredTodos = todoData.filter(
(todo) => todo.userId.toString() === selectedTodoUser
);
setFilteredTodoList(filteredTodos);
};
useEffect(() => {
const getTodoData = async () => {
console.log("useeffect");
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/todos"
);
setTodoData(response.data);
setFilteredTodoList(response.data);
} catch (error) {
console.log(error);
}
};
getTodoData();
}, []);
const handleSelect = (todoUser: string) => {
setSelectedTodoUser(todoUser);
filterTodos();
};
return (
<div className="main">
<TodoSelect onSelect={handleSelect} />
<h1>Todo List</h1>
<div>
{" "}
{filteredTodoList.map((todo) => (
<div>
<div>User: {todo.userId}</div>
<div>Title: {todo.title}</div>
</div>
))}
</div>
</div>
);
}
在TodoSelect.tsx
export default function TodoSelect({ onSelect }: TodoUsers) {
const users = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
return (
<div>
<span>User: </span>
<select
onChange={(e) => {
onSelect(e.target.value);
}}
>
{users.map((item) => (
<option value={item} key={item}>
{item}
</option>
))}
</select>
</div>
);
}
状态更新不是同步发生的!因此,filterTodos
函数中的 selectedTodoUser
不是您期望的那样,因为状态尚未更新。
进行以下更改:
将todoUser
传递给filterTodos
:
const handleSelect = (todoUser: string) => {
setSelectedTodoUser(todoUser);
filterTodos(todoUser);
};
然后在 filterTodos
中使用传递的参数而不是状态进行比较。
const filterTodos = (todoUser) => {
let filteredTodos = todoData.filter(
(todo) => todo.userId.toString() === todoUser
);
setFilteredTodoList(filteredTodos);
};
您可能不再需要 selectedTodoUser
状态!
实际上根本不需要 filteredTodoList
,因为它很容易从 todoData
状态和 selectedTodoUser
状态导出。派生状态不属于状态。
见Identify the Minimal but Complete Representation of UI State
Let’s go through each one and figure out which one is state. Ask three
questions about each piece of data:
- Is it passed in from a parent via props? If so, it probably isn’t state.
- Does it remain unchanged over time? If so, it probably isn’t state.
- Can you compute it based on any other state or props in your component? If so, it isn’t state.
在将状态渲染到 UI 时过滤 todoData
内联。不要忘记向映射的待办事项添加 React 键。我假设每个 todo
对象都有一个 id
属性,但在您的数据集中使用任何唯一的 属性。
示例:
function App() {
const [todoData, setTodoData] = useState<Todo[]>([]);
const [selectedTodoUser, setSelectedTodoUser] = useState<string | null>(null);
useEffect(() => {
const getTodoData = async () => {
console.log("useeffect");
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/todos"
);
setTodoData(response.data);
} catch (error) {
console.log(error);
}
};
getTodoData();
}, []);
const handleSelect = (todoUser: string) => {
setSelectedTodoUser(todoUser);
};
return (
<div className="main">
<TodoSelect onSelect={handleSelect} />
<h1>Todo List</h1>
<div>
{filteredTodoList
.filter((todo) => todo.userId.toString() === selectedTodoUser)
.map((todo) => (
<div key={todo.id}>
<div>User: {todo.userId}</div>
<div>Title: {todo.title}</div>
</div>
))
}
</div>
</div>
);
}
注意:我看到有人建议使用 useEffect 解决这个问题,但我不会在这里通过 useEffect 更新状态..
我遇到的问题是,例如,当用户选择 id 7 时,它会触发 App.tsx 中的一个函数并过滤待办事项列表数据并使用过滤后的列表更新状态。但是在浏览器中,它不会立即反映更新状态。它落后了一步。
这是一个Demo
如何解决这个问题(不合并 App.tsx 和 TodoSelect.tsx)?
function App() {
const [todoData, setTodoData] = useState<Todo[]>([]);
const [filteredTodoList, setFilteredTodoList] = useState<Todo[]>([]);
const [selectedTodoUser, setSelectedTodoUser] = useState<string | null>(null);
const filterTodos = () => {
let filteredTodos = todoData.filter(
(todo) => todo.userId.toString() === selectedTodoUser
);
setFilteredTodoList(filteredTodos);
};
useEffect(() => {
const getTodoData = async () => {
console.log("useeffect");
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/todos"
);
setTodoData(response.data);
setFilteredTodoList(response.data);
} catch (error) {
console.log(error);
}
};
getTodoData();
}, []);
const handleSelect = (todoUser: string) => {
setSelectedTodoUser(todoUser);
filterTodos();
};
return (
<div className="main">
<TodoSelect onSelect={handleSelect} />
<h1>Todo List</h1>
<div>
{" "}
{filteredTodoList.map((todo) => (
<div>
<div>User: {todo.userId}</div>
<div>Title: {todo.title}</div>
</div>
))}
</div>
</div>
);
}
在TodoSelect.tsx
export default function TodoSelect({ onSelect }: TodoUsers) {
const users = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
return (
<div>
<span>User: </span>
<select
onChange={(e) => {
onSelect(e.target.value);
}}
>
{users.map((item) => (
<option value={item} key={item}>
{item}
</option>
))}
</select>
</div>
);
}
状态更新不是同步发生的!因此,filterTodos
函数中的 selectedTodoUser
不是您期望的那样,因为状态尚未更新。
进行以下更改:
将todoUser
传递给filterTodos
:
const handleSelect = (todoUser: string) => {
setSelectedTodoUser(todoUser);
filterTodos(todoUser);
};
然后在 filterTodos
中使用传递的参数而不是状态进行比较。
const filterTodos = (todoUser) => {
let filteredTodos = todoData.filter(
(todo) => todo.userId.toString() === todoUser
);
setFilteredTodoList(filteredTodos);
};
您可能不再需要 selectedTodoUser
状态!
实际上根本不需要 filteredTodoList
,因为它很容易从 todoData
状态和 selectedTodoUser
状态导出。派生状态不属于状态。
见Identify the Minimal but Complete Representation of UI State
Let’s go through each one and figure out which one is state. Ask three questions about each piece of data:
- Is it passed in from a parent via props? If so, it probably isn’t state.
- Does it remain unchanged over time? If so, it probably isn’t state.
- Can you compute it based on any other state or props in your component? If so, it isn’t state.
在将状态渲染到 UI 时过滤 todoData
内联。不要忘记向映射的待办事项添加 React 键。我假设每个 todo
对象都有一个 id
属性,但在您的数据集中使用任何唯一的 属性。
示例:
function App() {
const [todoData, setTodoData] = useState<Todo[]>([]);
const [selectedTodoUser, setSelectedTodoUser] = useState<string | null>(null);
useEffect(() => {
const getTodoData = async () => {
console.log("useeffect");
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/todos"
);
setTodoData(response.data);
} catch (error) {
console.log(error);
}
};
getTodoData();
}, []);
const handleSelect = (todoUser: string) => {
setSelectedTodoUser(todoUser);
};
return (
<div className="main">
<TodoSelect onSelect={handleSelect} />
<h1>Todo List</h1>
<div>
{filteredTodoList
.filter((todo) => todo.userId.toString() === selectedTodoUser)
.map((todo) => (
<div key={todo.id}>
<div>User: {todo.userId}</div>
<div>Title: {todo.title}</div>
</div>
))
}
</div>
</div>
);
}