我正在尝试使用React Hooks
重构代码,但我真的不理解如何使用Hooks通过React Routers将props传递给组件。
旧的(正常)React代码如下所示:
App.js
import React from 'react';
import { withRouter } from "react-router-dom";
import {Routes} from './routes/Routes';
function App() {
const childProps={something: "else"};
return (
<div className="App">
<Routes childProps={childProps} />
</div>
);
}
export default withRouter(App);
Routes.js
import {Switch, Route} from 'react-router-dom';
import Game from '../game/Game';
import Scenario from '../game/Scenario';
const CustomRoute = ({ component: C, props: cProps, ...rest }) =>
<Route
{...rest}
render={(props) =>
<C {...props} {...cProps} />
}
/>;
export const Routes = ({childProps}) =>
<Switch>
<Route path="/" exact component={Game} props={childProps} />
<CustomRoute path="/scenario/:id" exact component={Scenario} props={childProps}/>
</Switch>
Game.js
import React from 'react';
const Game = () => {
return (
<div className="Game">
<header className="Game-header">
<a href="/scenario/0">
START
</a>
</header>
</div>
);
};
export default Game;
Scenario.js
export default class Scenario extends Component {
constructor(props) {
super(props);
this.state = {
scenarios: null,
scenarioId: null,
currentScenario: null
}
}
async componentDidMount() {
const scenarioId = await this.props.match.params.id;
const scenarios = await data.scenarios;
this.setState({scenarios, scenarioId});
this.getScenario();
}
getScenario = () => {
this.state.scenarios.forEach((scenario) => {
if (scenario.id === this.state.scenarioId) {
const currentScenario = scenario;
this.setState({currentScenario});
}
})
}
render() {
return (
<div>
{this.state.currentScenario != null
? this.state.currentScenario.options.length === 1
? (
<div>
<div>{this.state.currentScenario.text}</div>
<div>{this.state.currentScenario.options[0].text}</div>
<a href="/">Go Back</a>
</div>
)
: (
<div>
<div>{this.state.currentScenario.text}</div>
<div>{this.state.currentScenario.options.map((option, index) => (
<div key={index}>
<a href={`/scenario/${option.to}`}>
{option.text}
</a>
</div>
))}</div>
</div>
)
: null
}
</div>
);
}
};
所以我在网上找到了这段代码,它将改变我从路由器获得道具的方式:
HookRouter.js
import * as React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
const RouterContext = React.createContext(null);
export const HookedBrowserRouter = ({ children }) => (
<BrowserRouter>
<Route>
{(routeProps) => (
<RouterContext.Provider value={routeProps}>
{children}
</RouterContext.Provider>
)}
</Route>
</BrowserRouter>
);
export function useRouter() {
return React.useContext(RouterContext);
};
新App.js
import React from 'react';
import { withRouter } from "react-router-dom";
import {Routes} from './routes/Routes';
import {HookedBrowserRouter, useRouter} from './routes/HookRouter';
function App() {
const childProps={something: "else"};
return (
<HookedBrowserRouter>
<div className="App">
<Routes childProps={childProps} />
</div>
</HookedBrowserRouter>
);
}
export default withRouter(App);
我借助新的Scenario.js达到了目标
import React, { Component, useState, useEffect } from 'react';
import data from '../data/fake';
import {useRouter} from '../routes/HookRouter';
const RouterContext = React.createContext(null);
const HookSceneario = () => {
const [scenarios, setScenarios] = useState(null);
const [scenarioId, setScenarioId] = useState(null);
const [currentScenario, setCurrentScenario] = useState(null);
// Similar to componentDidMount and componentDidUpdate:
// Update the document title using the browser API
// console.log(React.useContext(RouterContext));
useEffect(() => {
console.log(scenarios);
});
return (
<div>
// ...
</div>
);
}
因此,useState
替换了类构造函数中的this.state
,而useEffect
应该替换了componentDidMount
,但我找不到获取{{1 }}。
答案 0 :(得分:1)
我认为这很好地说明了您正在尝试做什么:
记住:
由<Route>
呈现的组件始终可以访问routeProps
(匹配,位置和历史记录)。
如果它是由component
道具渲染的,例如<Route ... component={Home}/>
,这是自动的。
如果它是由render
道具渲染的,则需要对其进行传播,如下所示:
// You can spread routeProps to make them available to your rendered Component
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={routeProps => (
<FadeIn>
<Component {...routeProps}/>
</FadeIn>
)}/>
)
结果:
完整代码:
index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router } from "react-router-dom";
import AllRoutes from "./AllRoutes";
function App() {
return (
<Router>
<AllRoutes />
</Router>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
AllRoutes.js
import React from "react";
import { Switch, Route } from "react-router-dom";
import Home from "./Home";
import Component1 from "./Component1";
function AllRoutes() {
return (
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/comp1" component={Component1} />
</Switch>
);
}
export default AllRoutes;
Home.js
import React from "react";
import { Link } from "react-router-dom";
function Home(props) {
return (
<div>
I am HOME component
<ul>
<li>
<Link to={"/comp1"}>Component1</Link>
</li>
</ul>
I have access to routeProps: YES
<br />
Because I'm directly rendered from a Route
<ul>
<li>{"props.match:" + props.match.toString()}</li>
<li>{"props.location:" + props.location.toString()}</li>
<li>{"props.history:" + props.history.toString()}</li>
</ul>
</div>
);
}
export default Home;
Component1.js
import React from "react";
import { Link } from "react-router-dom";
import Component1Child from "./Component1Child";
import RouterContext from "./RouterContext";
function Component1(props) {
const routeProps = {
match: props.match,
history: props.history,
location: props.location
};
return (
<RouterContext.Provider value={routeProps}>
<div>
<b>I am Component1</b>
<ul>
<li>
<Link to={"/"}>Home</Link>
</li>
</ul>
I have access to routeProps: YES
<br />
Because I'm directly rendered from a Route.
<br />
And I automatically 'inherit' them when I'm rendered through the Route
'component' prop
<ul>
<li>{"props.match:" + props.match.toString()}</li>
<li>{"props.location:" + props.location.toString()}</li>
<li>{"props.history:" + props.history.toString()}</li>
</ul>
<Component1Child />
</div>
</RouterContext.Provider>
);
}
export default Component1;
Component1Child.js
import React from "react";
import Component1GrandChild from "./Component1GrandChild";
function Component1Child(props) {
return (
<div>
<b>I am Component1Child</b> <br />
<br />
I have access to routeProps: NO
<br />
Because I'm NOT directly rendered from a Route.
<br />I am rendered by Componen1 and routeProps are not automatically
passed down.
<ul>
<li>{"props.match:" + props.match}</li>
<li>{"props.location:" + props.location}</li>
<li>{"props.history:" + props.history}</li>
</ul>
<Component1GrandChild />
</div>
);
}
export default Component1Child;
Component1GrandChild.js
import React from "react";
import useRouteProps from "./useRouteProps";
function Component1GrandChild(props) {
const [match, location, history] = useRouteProps();
return (
<div>
<b>I am Component1GrandChild</b> <br />
<br />
I have access to routeProps: YES
<br />
Because I'm consuming the routeProps provided by Component1 (which is the
one directly rendered by the Route)
<br /> And I'm consuming that through a custom hook called useRouteProps.
<br />I am rendered by Componen1 and routeProps are not automatically
passed down.
<ul>
<li>{"props.match:" + match}</li>
<li>{"props.location:" + location}</li>
<li>{"props.history:" + history}</li>
</ul>
</div>
);
}
export default Component1GrandChild;
RouterContext.js
import React from "react";
const RouterContext = React.createContext(null);
export default RouterContext;
useRouteProps.js
import { useContext } from "react";
import RouterContext from "./RouterContext";
function useRouteProps() {
const routeProps = useContext(RouterContext);
return [routeProps.match, routeProps.location, routeProps.history];
}
export default useRouteProps;