Note: I've seen people suggesting useEffect to this issue but I am not updating the state through useEffect here..
The problem I am having is that when a user selects id 7 for example, it triggers a function in App.tsx and filters the todo list data and update the state with the filtered list. But in the browser, it doesn't reflect the updated state immediately. It renders one step behind.
Here is a Demo
How do I fix this issue (without combining App.tsx and 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>
);
}
In 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>
);
}
CodePudding user response:
There's actually no need at all for the filteredTodoList
since it is easily derived from the todoData
state and the selectedTodoUser
state. Derived state doesn't belong in state.
See 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.
Filter the todoData
inline when rendering state out to the UI. Don't forget to add a React key to the mapped todos. I'm assuming each todo
object has an id
property, but use any unique property in your data set.
Example:
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>
);
}
CodePudding user response:
State update doesn't happen synchronously! So, selectedTodoUser
inside filterTodos
function is not what you're expecting it to be because the state hasn't updated yet.
Make the following changes:
Pass todoUser
to filterTodos
:
const handleSelect = (todoUser: string) => {
setSelectedTodoUser(todoUser);
filterTodos(todoUser);
};
And then inside filterTodos
compare using the passed argument and not with the state.
const filterTodos = (todoUser) => {
let filteredTodos = todoData.filter(
(todo) => todo.userId.toString() === todoUser
);
setFilteredTodoList(filteredTodos);
};
You probably won't need the selectedTodoUser
state anymore!