我正在做一个React项目,该项目要求应用程序URL是完全声明的、静态的和不可变的,所以:
https://exampleurl.com/path/to/app/index.html
但没有以下内容:
https://exampleurl.com/path/to/app/
https://exampleurl.com/path/to/app/index.html?query=value
https://exampleurl.com/path/to/app/subdir/index.html
因此,该项目使用state
和withRouter
来提供基本的应用程序导航。负责的组成部分如下:
import React from 'react';
import { withRouter} from "react-router-dom";
import RenderNav from './RenderNav';
import Cohort from '../view/Cohort';
import Holdings from '../view/Holdings';
import Policy from '../view/Policy';
import Search from '../view/Search';
import Start from '../view/Start';
import Training from '../view/Training';
import WatchList from '../view/WatchList';
class RenderDisplay extends React.Component {
constructor(props) {
super(props);
this.state = this.props.location.state || { activeDisplay: 'start' };
this.props.history.replace(this.props.location.pathname, this.state);
}
componentDidUpdate(prevProps, prevState) {
if (this.props.location !== prevProps.location) {
this.setState(this.props.location.state);
}
}
setDisplay() {
let displayMode = [];
let d = this.state.activeDisplay;
switch(d) {
case 'start': displayMode = [
<Start key={`display-${d}`} />
]; break;
// ...and so on - activeDisplay determines displayMode (and so screen contents)
default: displayMode = [<div>Error</div>]; break;
}; return displayMode;
}
render() {
return (
<div className="container">
{this.setDisplay()}
</div>
);
}
}
export default withRouter(RenderDisplay);
location.state
对象由<Link />
组件设置:
<Link to={{ state: {activeDisplay: 'start'} }}>
<button type="button">Home</button>
</Link>
结果是在URL保持不变的情况下,在整个应用程序中进行正常的浏览器导航。
我的问题是:如何将上面基于类的RenderDisplay
组件转换为功能组件(使用钩子(?
我已经尝试了useEffect
、useState
和useLocation
的几种排列,但我显然不太了解钩子,无法重现上面基于类的组件的行为。
所以,我们总是需要做的一些步骤:
- 不需要HOCs,特别是
withRouter
。我们可以使用useHistory
挂钩 - 构造函数或
componentDidMount
的所有初始化都可以直接在状态中完成,也可以在只运行一次的useEffect
(空依赖数组(中完成 - 所有对状态的更新,特别是来自
componentDidUpdate
的更新,都可以在useEffect
中完成,这取决于您的状态变量。在您的特定情况下,您还需要以前的prop
。在这种情况下,我们可以创建一个自定义钩子并使用它。在我的情况下,它将是usePrev
- 所有清理任务,特别是来自
componentWillUnmount
的清理任务,都可以包含在返回清理操作的useEffect
中
记住这些事情,这些可能是需要做的更改:
注意:在此过程中,我意识到您正在尝试渲染数组。我修改了它以渲染单个元素,否则您可以更新它。
import {useRef} from "react";
export const usePrev = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
import React, {useState, useEffect} from 'react';
import {useHistory, useLocation} from 'react-router-dom';
import RenderNav from './RenderNav';
import Cohort from '../view/Cohort';
import Holdings from '../view/Holdings';
import Policy from '../view/Policy';
import Search from '../view/Search';
import Start from '../view/Start';
import Training from '../view/Training';
import WatchList from '../view/WatchList';
import usePrev from './usePrev';
const RenderDisplay = (props) => {
const history = useHistory();
const location = useLocation();
const prevLocation = usePrev(location);
const [componentState, setComponentState] = useState(location.state || { activeDisplay: 'start' });
useEffect(() => {
history.replace(location.pathname, componentState);
}, [location.pathname, history]);
useEffect(() => {
if (location.state.activeDisplay !== prevLocation.state.activeDisplay) {
setComponentState(location.state);
}
}, [location]);
setDisplay() {
let displayMode;
let d = componentState.activeDisplay;
switch(d) {
case 'start': displayMode = <Start key={`display-${d}`} />;
break;
// ...and so on - activeDisplay determines displayMode (and so screen contents)
default: displayMode = <div>Error</div>; break;
};
return displayMode;
}
return (
<div className="container">
{setDisplay()}
</div>
);
}
export default RenderDisplay;
你能试试这个吗?您可以通过这种方式将类组件转换为函数。如果这有帮助,请告诉我。
import { useEffect, useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
function RenderDisplay() {
const location = useLocation();
const history = useHistory();
const [state, setState] = useState(
location.state || { activeDisplay: "start" }
);
useEffect(() => {
history.replace(location.pathname, state);
}, [history, location, state]);
const setDisplay = () => {
let displayMode = [];
const d = state.activeDisplay;
switch (d) {
case "start":
displayMode = [<Start key={`display-${d}`} />];
break;
// ...and so on - activeDisplay determines displayMode (and so screen contents)
default:
displayMode = [<div>Error</div>];
break;
}
return displayMode;
};
return <div className="container">{setDisplay()}</div>;
}
export default RenderDisplay;
也适用于您的这段代码,
componentDidUpdate(prevProps, prevState) {
if (this.props.location !== prevProps.location) {
this.setState(this.props.location.state);
}
}
我们需要处理另一个useEffect
钩子。