ค้นหาเว็บไซต์

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 (
    ...
  );
}