【问题标题】:State not saving when using React Hooks with React Router将 React Hooks 与 React Router 一起使用时状态未保存
【发布时间】:2020-08-29 20:25:33
【问题描述】:

我正在尝试从在我的新项目中使用 redux 切换到使用钩子。我的理解是,我可以使用一个自定义钩子,例如这个钩子,从各种组件中加载它以访问相同的状态,类似于 redux 让我访问状态的方式。

import { useState } from 'react';

export const useSelectedStore = () => {
  const [selectedStore, setSelectedStore] = useState();

  return [
    selectedStore,
    setSelectedStore,
  ];
};

我遇到的问题是我有一个页面,其中包含一个项目,当用户单击一个项目时,我需要设置所选商店,然后将它们重定向到该页面。这是组件内的点击操作:

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Home from './pages/Home/Home';
import Stores from './pages/Stores/Stores';
import StoreDetails from './pages/StoreDetails/StoreDetails';

function App() {
  return (
    <Router>
      <div>
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/stores">
            <Stores />
          </Route>
          <Route path="/store-details">
            <StoreDetails />
          </Route>
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

export default App;

import React, { useState } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';

import { useSelectedStore } from '../../hooks/useSelectedStore';

function Stores(props) {
  const { history } = props;

  const [selectedStore, setSelectedStore] = useSelectedStore();

  const goToStoreDetails = (index) => {
    // Now add our store data
    setSelectedStore(storeRequestDetails.stores[index]);
    console.log(selectedStore);

    history.push('/store-details');
  };

控制台日志输出未定义,因为尚未设置存储,因此历史推送发生在值真正设置之前。

我尝试在组件中使用这样的 useEffect 来获取更改并在重定向之前处理更改,但状态正在为 store-details 页面的组件上重置。

import React, { useEffect, useState } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';

import { useSelectedStore } from '../../hooks/useSelectedStore';

function Stores(props) {
  const { history } = props;

  const [selectedStore, setSelectedStore] = useSelectedStore();

  useEffect(() => {
    if (selectedStore) {
    console.log(selectedStore);
      history.push('/store-details');
    }
  }, [selectedStore]);

  const goToStoreDetails = (index) => {
    // Now add our store data
    setSelectedStore(storeRequestDetails.stores[index]);
  };

此处的控制台日志将显示实际选择的商店详细信息,但在下一页加载后再次显示状态现在再次未定义。

import React from 'react';

import { useSelectedStore } from '../../hooks/useSelectedStore';

function StoreDetails() {
  const [selectedStore, setSelectedStore] = useSelectedStore();
  console.log(selectedStore); // Returns undefined

如何在这两个组件之间正确共享状态?

【问题讨论】:

  • 很遗憾你的理解是不正确的。自定义钩子的每次调用都将通过在内部调用 useState 创建自己的状态。更像是您将自定义挂钩内的代码直接写入您正在使用它的组件中。为了您的目的,您将不得不使用反应上下文。

标签: javascript reactjs react-router react-hooks


【解决方案1】:

我的理解是,我可以使用一个自定义钩子,例如这个钩子,从各种组件中加载它以访问相同的状态,类似于 redux 让我访问状态的方式。

您展示的自定义钩子将导致多个组件各自创建自己的独立本地状态。这些类型的自定义钩子对于代码重用很有用,但它不允许组件共享数据。

如果你想在组件之间共享数据,一般的反应方法是将状态向上移动,直到它位于每个需要它的组件的共同祖先。然后可以通过道具或上下文传递数据。如果您希望数据在全球范围内可用,那么您会将其移动到组件树的顶部,并且几乎可以肯定会使用上下文而不是道具。

这就是 redux 所做的:您在树的顶部附近设置存储,然后它使用上下文使其可供后代使用。您可以使用类似的模式自己做类似的事情。在树的顶部附近的某个地方(可能在 App 中,但您也可以将其移动到其他地方),创建状态并通过上下文共享它:

export const SelectedStoreContext = React.createContext();

function App() {
  const value = useState();
  return (
    <SelectedStoreContext.Provider value={value}>
      <Router>
        <div>
          //etc
        </div>
      </Router>
    </StoreContext.Provider>
  );
}

并使用它:

function StoreDetails() {
  const [selectedStore, setSelectedStore] = useContext(SelectedStoreContext); 

如果你想调用这个 useSelectedStore 并稍微隐藏涉及上下文的事实,你可以创建一个这样的自定义钩子:

const useSelectedStore = () => useContext(SelectedStoreContext);

// used like
const [selectedStore, setSelectedStore] = useSelectedStore();

【讨论】:

  • 谢谢,这比只共享相同状态的钩子更有意义。
【解决方案2】:

海关挂钩不共享任何数据。它们只是内置钩子的组合。但是你仍然可以使用 react context 来分享 store 状态:

/* StoreContext.js */

const StoreContext = createContext(null);

export const StoreProvider = ({children}) => {
    const storeState = useState();

    return (
        <StoreContext.Provider value={storeState}>
            {children}
        </StoreContext.Provider>
    );
};

export const useStore = () => useContext(StoreContext);

用法:

/* App.js */
/* wrap your components using the store in the StoreProvider */

import {StoreProvider} from './StoreContext';

const App = () => (
    <StoreProvider>
        {/* some children */}
    </StoreProvider>
);

/* StoreDetails.js */
/* use the useStore hook in your components */

import {useStore} from './StoreContext';

function StoreDetails() {
  const [selectedStore, setSelectedStore] = useStore(); 

  // ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2019-08-08
    • 2018-12-30
    • 2019-11-24
    • 2021-07-09
    • 2021-12-09
    相关资源
    最近更新 更多