我正在尝试从在新项目中使用 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 来获取更改并在重定向之前处理更改,但存储详细信息页面的组件上的状态正在重置。
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
如何在这两个组件之间正确共享状态?
我的理解是,我可以使用像这样的自定义钩子从各种组件加载它以访问相同的状态,类似于 redux 让我访问状态的方式。
您展示的自定义挂钩将导致多个组件各自创建自己独立的本地状态。这些类型的自定义挂钩对于代码重用很有用,但它不允许组件共享数据。
如果要在组件之间共享数据,一般的 react 方法是将状态向上移动到树中,直到它位于需要它的每个组件的共同祖先处。然后,数据可以通过道具或上下文传递下来。如果您希望数据全局可用,那么您可以将其移动到组件树的顶部,并且几乎肯定会使用上下文而不是 props。
这就是 redux 的作用:您在树的顶部附近设置存储,然后它使用上下文使其可供后代使用。通过使用类似的模式,您可以自己执行类似的操作。在树顶部附近的某个地方(也许在应用程序中,但您也可以将其移动到其他地方(,创建状态,并通过上下文共享它:
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();
海关钩子不共享任何数据。它们只是内置钩子的组合。但是您仍然可以使用 react 上下文共享存储状态:
/* 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();
// ...
}