我正在开发一个 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
的几种排列,但我显然不太了解钩子,无法重现上面基于类的组件的行为.
答案 0 :(得分:2)
所以,一些我们总是需要做的步骤:
withRouter
。我们可以改用 useHistory
钩子。componentDidMount
的所有初始化都可以直接在 state 中完成,也可以在只运行一次的 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;
答案 1 :(得分:0)
你能试试这个吗?您可以将类组件转换为这种方式。 如果这有帮助,请告诉我。
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
钩子。