React Router v6.10切换路由丢失页面状态问题求助
React路由切换后Todos数据丢失的原因及解决办法
我是React新手,正在学习路由相关知识。我做了一个简单应用,包含Home和Todos两个页面,通过Tab Bar切换路由。在Todos页面可以添加数据到列表,但切换到Home再切回Todos时,之前添加的数据就没了。用Ionic-React时不会出现这个问题,想知道原因和解决办法。
相关代码
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> ) }
问题原因
这是因为React Router切换路由时,未匹配的路由组件会被卸载。你的todosList状态存在Todos组件内部的useState里,当切换到Home页面,Todos组件被卸载,状态就会被销毁;切回Todos时,组件重新挂载,useState会重新初始化,所以数据就丢了。
而Ionic-React的路由默认带有组件缓存机制(类似Vue的keep-alive),切换路由时组件不会被卸载,状态自然保留。
解决办法
有几种常见方案:
1. 状态提升到父组件
把todosList的状态移到App.tsx中,通过props传递给Todos组件,这样组件卸载时状态存在父组件里,不会丢失:
修改App.tsx:
function App() { const router = useNavigate(); const [todosList, setTodosList] = useState<TodoClass[]>([]); const addNewTodo = (todo: TodoClass) => { setTodosList([...todosList, todo]); } 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 todosList={todosList} addNewTodo={addNewTodo}/>}></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:
interface TodosProps { todosList: TodoClass[]; addNewTodo: (todo: TodoClass) => void; } function Todos({ todosList, addNewTodo }: TodosProps) { 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(obj); changeInputValue(''); // 清空输入框 } 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} value={inputValue}/> </div> <div className='add-btn' onClick={handleAddTaskButtonClick}> ADD </div> </div> </div> ) }
2. 使用本地存储持久化数据
如果希望页面刷新后数据也不丢失,可以用localStorage或sessionStorage保存数据,组件挂载时读取,更新时同步存储:
修改Todos.tsx:
function Todos() { // 挂载时从localStorage读取数据 const [todosList, addNewTodo] = useState<TodoClass[]>(() => { const saved = localStorage.getItem('todosList'); return saved ? JSON.parse(saved) : []; }); 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}; const newList = [...todosList, obj]; addNewTodo(newList); // 同步到localStorage localStorage.setItem('todosList', JSON.stringify(newList)); changeInputValue(''); } 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} value={inputValue}/> </div> <div className='add-btn' onClick={handleAddTaskButtonClick}> ADD </div> </div> </div> ) }
3. 使用全局状态管理
如果应用规模较大,可以用React Context、Redux、Zustand等全局状态管理工具,把todos数据放到全局状态中,任何组件都可以访问和修改。
内容的提问来源于stack exchange,提问作者akamzel
相关产品推荐
相关产品推荐

