React Router v6.10 – 更改路由会丢失上一页的状态

huangapple go评论69阅读模式
英文:

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&#39;m fairly new to React and I&#39;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>

  &lt;nav className=&#39;tab-bar&#39;&gt;
    &lt;div className=&#39;tab&#39; onClick={()=&gt;{router(&#39;/home&#39;)}}&gt;HOME&lt;/div&gt;
    &lt;div className=&#39;tab&#39;  onClick={()=&gt;{router(&#39;/todos&#39;)}}&gt;TODOS&lt;/div&gt;
  &lt;/nav&gt;


&lt;/div&gt;

)


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>

  &lt;div className=&#39;add-todo&#39;&gt;
      &lt;div className=&#39;input-todo&#39;&gt;
        &lt;input type=&quot;text&quot; placeholder=&#39;Input a todo task&#39; onChange={handleChangeEvent} /&gt;
      &lt;/div&gt;
      &lt;div className=&#39;add-btn&#39; onClick={handleAddTaskButtonClick}&gt;
        ADD
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

)
}


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&#39;s why I suppose that I am missing something)


</details>


# 答案1
**得分**: 0

`todos` 状态仅在 `Todos` 组件中是本地状态,当您从 `&quot;/todos&quot;` 导航到 `&quot;/home&quot;` 时,todos 路由不再匹配,因此 `Todos` 组件将卸载。React 状态不会持久保存。

您可以将 `todos` 状态提升到一个常驻的共同祖先,无论匹配什么路由,都保持挂载并将状态作为 props 传递。

示例:

```jsx
const navigate = useNavigate();

const [todosList, addNewTodo] = useState&lt;TodoClass[]&gt;([]);

const addTodo = (todo: TodoClass) =&gt; {
  addNewTodo(todos =&gt; [...todos, todo]);
};

return (
  &lt;div className=&#39;app-main-cont&#39;&gt;
    ...
  &lt;/div&gt;
);
interface TodosProps {
  todosList: TodoClass[];
  addTodo: TodoClass =&gt; void;
}

function Todos({ addTodo, todosList }: TodosProps) {
  const [inputValue, changeInputValue] = useState(&#39;&#39;);

  const handleChangeEvent = (event: ChangeEvent) =&gt; {
    const val = event.target as HTMLInputElement;
    changeInputValue(val.value);
  }

  const handleAddTaskButtonClick = (event: any) =&gt; {
    addTodo({ name: inputValue });
  }

  return (
    &lt;div className=&#39;todos-cont&#39;&gt;
      ...
    &lt;/div&gt;
  );
};

另一种选择可能是将本地 todosList 状态持久化到 localStorage,并从 localStorage 初始化状态。当组件挂载时,todosList 状态设置为持久化值或初始状态值 []。使用 useEffect 钩子在更新时将状态持久化到 localStorage。

示例:

function Todos() {
  const [todosList, addNewTodo] = useState&lt;TodoClass[]&gt;(() =&gt; {
    return JSON.parse(localStorage.getItem(&quot;todosList&quot;)) ?? [];
  });

  useEffect(() =&gt; {
    if (todosList) {
      localStorage.setItem(&quot;todosList&quot;, JSON.stringify(todosList));
    }
  }, [todosList]);

  ...
  
  return (
    ...
  );
}
英文:

The todos state is only local state in the Todos component, and when you navigate from &quot;/todos&quot; to &quot;/home&quot; 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&lt;TodoClass[]&gt;([]);

const addTodo = (todo: TodoClass) =&gt; {
  addNewTodo(todos =&gt; [...todos, todo]);
};

return (
  &lt;div className=&#39;app-main-cont&#39;&gt;
    &lt;div className=&#39;app-main-content&#39;&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;Navigate replace to=&quot;/home&quot; /&gt;} /&gt;
        &lt;Route path=&quot;/home&quot; element={&lt;Home /&gt;} /&gt;
        &lt;Route
          path=&quot;/todos&quot;
          element={(
            &lt;Todos
              todosList={todosList}
              addTodo={addTodo}
            /&gt;
          )}
        /&gt;
      &lt;/Routes&gt;
    &lt;/div&gt;

    &lt;nav className=&#39;tab-bar&#39;&gt;
      &lt;div className=&#39;tab&#39; onClick={() =&gt; router(&quot;/home&quot;)}&gt;HOME&lt;/div&gt;
      &lt;div className=&#39;tab&#39;  onClick={() =&gt; router(&quot;/todos&quot;)}&gt;TODOS&lt;/div&gt;
    &lt;/nav&gt;
  &lt;/div&gt;
);
interface TodosProps {
  todosList: TodoClass[];
  addTodo: TodoClass =&gt; void;
}

function Todos({ addTodo, todoList }: TodosProps) {
  const [inputValue, changeInputValue] = useState(&#39;&#39;);

  const handleChangeEvent = (event: ChangeEvent) =&gt; {
    const val = (event.target) as HTMLInputElement;
    changeInputValue(val.value);
  }

  const handleAddTaskButtonClick = (event: any) =&gt; {
    addTodo({ name: inputValue });
  }
    
  return (
    &lt;div className=&#39;todos-cont&#39;&gt;
      &lt;div className=&#39;list&#39;&gt;
        {todosList.map((item, index) =&gt; (
          &lt;div className=&#39;list-item&#39; key={index}&gt;
            {item.name}
          &lt;/div&gt;
        ))}
      &lt;/div&gt;

      &lt;div className=&#39;add-todo&#39;&gt;
        &lt;div className=&#39;input-todo&#39;&gt;
          &lt;input type=&quot;text&quot; placeholder=&#39;Input a todo task&#39; onChange={handleChangeEvent} /&gt;
        &lt;/div&gt;
        &lt;div className=&#39;add-btn&#39; onClick={handleAddTaskButtonClick}&gt;
          ADD
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

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&lt;TodoClass[]&gt;(() =&gt; {
    // Return persisted state value or default initial value
    return JSON.parse(localStorage.getItem(&quot;todosList&quot;)) ?? [];
  });

  useEffect(() =&gt; {
    if (todosList) {
      // Persist todosList state to localStorage
      localStorage.setItem(&quot;todosList&quot;, JSON.stringify(todosList));
    }
  }, [todosList]);

  ...
    
  return (
    ...
  );
}

huangapple
  • 本文由 发表于 2023年4月6日 21:09:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/75949910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定