reactjs - React Router v6.10 - การเปลี่ยนเส้นทางทำให้สูญเสียสถานะของหน้าก่อนหน้า
ผมค่อนข้างใหม่กับ React และตอนนี้กำลังเรียนรู้เกี่ยวกับ routing ผมสร้างแอปง่ายๆ ที่มีสองหน้า (หน้าแรกและ Todos) และผมสลับระหว่างหน้าผ่าน Tab Bar ในหน้าหนึ่งผมมีรายการที่สามารถเพิ่มข้อมูลเข้าไปได้ผ่านเหตุการณ์ โค้ดคือ:
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>
)
}
ตอนนี้ปัญหาคือเมื่อฉันเพิ่มข้อมูลลงในรายการ ไปที่หน้าแรกแล้วกลับมาที่หน้าทาสก์ผ่านแถบนำทาง ข้อมูลที่ฉันเพิ่มก่อนหน้านั้นหายไป ทำไมถึงเป็นแบบนั้นล่ะ? ฉันพลาดอะไรไปหรือเปล่า? ขอบคุณ! (สิ่งเดียวกันนี้ถ้าใช้ Ionic-React ไม่เกิดขึ้น นั่นจึงทำให้ฉันคิดว่าฉันพลาดบางอย่าง)
คำตอบ #1
สถานะ todos เป็นเพียงสถานะท้องถิ่นในคอมโพเนนต์ Todos และเมื่อคุณนำทางจาก "/todos" ไปยัง "/home" เส้นทาง todos จะไม่ตรงกันอีกต่อไป ดังนั้นคอมโพเนนต์ Todos จะถูกยกเลิกการติดตั้ง สถานะของ React จะไม่ถูกเก็บไว้
คุณสามารถยกสถานะ todos ขึ้นไปยังบรรพบุรุษร่วมที่ยังคงถูกติดตั้งไม่ว่ามีการจับคู่เส้นทางใด และส่งสถานะลงไปเป็น props
ตัวอย่าง :
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>
);
};
อีกทางเลือกหนึ่งอาจเป็นการเก็บสถานะ todosList ในเครื่องไว้ที่ localStorage และเริ่มต้นสถานะจาก localStorage เมื่อคอมโพเนนต์ถูก mount สถานะ todosList จะถูกตั้งค่าเป็นค่าที่เก็บไว้หรือค่าของสถานะเริ่มต้น [] ใช้ hook useEffect เพื่อเก็บสถานะลงใน localStorage เมื่อมีการอัปเดต
ตัวอย่าง :
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 (
...
);
}
