英文:
React Router v6.10 - changing route loses state of previous page
问题
所以我对React还比较新,现在我正在学习路由。我创建了一个简单的应用程序,其中有两个页面(Home和Todos),我通过选项卡栏在它们之间进行路由。在一个页面上,我有一个列表,我可以通过事件向其添加数据。代码如下:
App.tsx
```jsx
const router = useNavigate();
return (
<div className='app-main-cont'>
<div className='app-main-content'>
<Routes>
<Route path='/' element={<Navigate replace to='/home'/>}></Route>
<Route path='/home' element={<Home/>}></Route>
<Route path='/todos' element={<Todos/>}></Route>
</Routes>
</div>
<nav className='tab-bar'>
<div className='tab' onClick={() => {router('/home')}}>HOME</div>
<div className='tab' onClick={() => {router('/todos')}}>TODOS</div>
</nav>
</div>
)
Todos.tsx
function Todos() {
const [todosList, addNewTodo] = useState<TodoClass[]>([]);
const [inputValue, changeInputValue] = useState('');
const handleChangeEvent = (event: ChangeEvent) => {
const val = (event.target) as HTMLInputElement;
changeInputValue(val.value);
}
const handleAddTaskButtonClick = (event: any) => {
let obj: TodoClass = {name: inputValue};
addNewTodo([...todosList, obj]);
}
return (
<div className='todos-cont'>
<div className='list'>
{todosList.map((item, index) => {
return (
<div className='list-item' key={index}>
{item.name}
</div>
)
})}
</div>
<div className='add-todo'>
<div className='input-todo'>
<input type="text" placeholder='Input a todo task' onChange={handleChangeEvent} />
</div>
<div className='add-btn' onClick={handleAddTaskButtonClick}>
ADD
</div>
</div>
</div>
)
}
现在的问题是,当我向列表中添加数据,转到主页,然后通过导航栏返回到Todos页面时,之前添加的数据会丢失。为什么会这样?我漏掉了什么吗?谢谢!(使用Ionic-React时没有发生同样的情况,这就是我认为我漏掉了什么的原因)
<details>
<summary>英文:</summary>
So I'm fairly new to React and I'm now learning about routing. I created a simple app where I have two pages (Home and Todos) and I route between them through a Tab Bar. On one page I have a list where I can add data to it through events. Code is:
App.tsx
const router = useNavigate();
return (
<div className='app-main-cont'>
<div className='app-main-content'>
<Routes>
<Route path='/' element={<Navigate replace to='/home'/>}></Route>
<Route path='/home' element={<Home/>}></Route>
<Route path='/todos' element={<Todos/>}></Route>
</Routes>
</div>
<nav className='tab-bar'>
<div className='tab' onClick={()=>{router('/home')}}>HOME</div>
<div className='tab' onClick={()=>{router('/todos')}}>TODOS</div>
</nav>
</div>
)
Todos.tsx
function Todos() {
const [todosList, addNewTodo] = useState<TodoClass[]>([]);
const [inputValue, changeInputValue] = useState('');
const handleChangeEvent = (event: ChangeEvent) => {
const val = (event.target) as HTMLInputElement;
changeInputValue(val.value);
}
const handleAddTaskButtonClick = (event: any) => {
let obj: TodoClass = {name: inputValue};
addNewTodo([...todosList, obj]);
}
return (
<div className='todos-cont'>
<div className='list'>
{todosList.map((item, index) => {
return (
<div className='list-item' key={index}>
{item.name}
</div>
)
})}
</div>
<div className='add-todo'>
<div className='input-todo'>
<input type="text" placeholder='Input a todo task' onChange={handleChangeEvent} />
</div>
<div className='add-btn' onClick={handleAddTaskButtonClick}>
ADD
</div>
</div>
</div>
)
}
Now the problem is that when I add data to the list, go to Home and then get back to the Todos page through the navbar, the data that I added before is lost. Why is that? Am I missing something? Thanks!
(The same thing using Ionic-React is not happening that's why I suppose that I am missing something)
</details>
# 答案1
**得分**: 0
`todos` 状态仅在 `Todos` 组件中是本地状态,当您从 `"/todos"` 导航到 `"/home"` 时,todos 路由不再匹配,因此 `Todos` 组件将卸载。React 状态不会持久保存。
您可以将 `todos` 状态提升到一个常驻的共同祖先,无论匹配什么路由,都保持挂载并将状态作为 props 传递。
示例:
```jsx
const navigate = useNavigate();
const [todosList, addNewTodo] = useState<TodoClass[]>([]);
const addTodo = (todo: TodoClass) => {
addNewTodo(todos => [...todos, todo]);
};
return (
<div className='app-main-cont'>
...
</div>
);
interface TodosProps {
todosList: TodoClass[];
addTodo: TodoClass => void;
}
function Todos({ addTodo, todosList }: TodosProps) {
const [inputValue, changeInputValue] = useState('');
const handleChangeEvent = (event: ChangeEvent) => {
const val = event.target as HTMLInputElement;
changeInputValue(val.value);
}
const handleAddTaskButtonClick = (event: any) => {
addTodo({ name: inputValue });
}
return (
<div className='todos-cont'>
...
</div>
);
};
另一种选择可能是将本地 todosList
状态持久化到 localStorage,并从 localStorage 初始化状态。当组件挂载时,todosList
状态设置为持久化值或初始状态值 []
。使用 useEffect
钩子在更新时将状态持久化到 localStorage。
示例:
function Todos() {
const [todosList, addNewTodo] = useState<TodoClass[]>(() => {
return JSON.parse(localStorage.getItem("todosList")) ?? [];
});
useEffect(() => {
if (todosList) {
localStorage.setItem("todosList", JSON.stringify(todosList));
}
}, [todosList]);
...
return (
...
);
}
英文:
The todos
state is only local state in the Todos
component, and when you navigate from "/todos"
to "/home"
the todos route is no longer matched so the Todos
component unmounts. React state is not persisted.
You could lift the todos
state up to a common ancestor that remains mounted regardless of what route is matched and pass the state down as props.
Example:
const navigate = useNavigate();
const [todosList, addNewTodo] = useState<TodoClass[]>([]);
const addTodo = (todo: TodoClass) => {
addNewTodo(todos => [...todos, todo]);
};
return (
<div className='app-main-cont'>
<div className='app-main-content'>
<Routes>
<Route path="/" element={<Navigate replace to="/home" />} />
<Route path="/home" element={<Home />} />
<Route
path="/todos"
element={(
<Todos
todosList={todosList}
addTodo={addTodo}
/>
)}
/>
</Routes>
</div>
<nav className='tab-bar'>
<div className='tab' onClick={() => router("/home")}>HOME</div>
<div className='tab' onClick={() => router("/todos")}>TODOS</div>
</nav>
</div>
);
interface TodosProps {
todosList: TodoClass[];
addTodo: TodoClass => void;
}
function Todos({ addTodo, todoList }: TodosProps) {
const [inputValue, changeInputValue] = useState('');
const handleChangeEvent = (event: ChangeEvent) => {
const val = (event.target) as HTMLInputElement;
changeInputValue(val.value);
}
const handleAddTaskButtonClick = (event: any) => {
addTodo({ name: inputValue });
}
return (
<div className='todos-cont'>
<div className='list'>
{todosList.map((item, index) => (
<div className='list-item' key={index}>
{item.name}
</div>
))}
</div>
<div className='add-todo'>
<div className='input-todo'>
<input type="text" placeholder='Input a todo task' onChange={handleChangeEvent} />
</div>
<div className='add-btn' onClick={handleAddTaskButtonClick}>
ADD
</div>
</div>
</div>
);
};
An alternative might also be to persist the local todosList
state to localStorage, and initialize the state from localStorage. When the component is mounted the todosList
state is set to the persisted value or the initial state value []
. Use a useEffect
hook to persist state to localStorage when it updates.
Example:
function Todos() {
const [todosList, addNewTodo] = useState<TodoClass[]>(() => {
// Return persisted state value or default initial value
return JSON.parse(localStorage.getItem("todosList")) ?? [];
});
useEffect(() => {
if (todosList) {
// Persist todosList state to localStorage
localStorage.setItem("todosList", JSON.stringify(todosList));
}
}, [todosList]);
...
return (
...
);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论