Kev*_*vin 6 reactjs react-router react-router-dom
当使用 React 和 React Router 时,我遇到了一些安装问题。这甚至可能不是 React Router 本身的问题。我想与子路由一起传递一些额外的数据。这似乎有效,但是每次更改状态时,主页上的更改都会触发孙子重新安装。
为什么会这样,为什么这只会发生在孙辈身上,而不仅仅是孩子们身上?
代码示例:
import React, { useEffect, useState } from 'react';
import { Route, Switch, BrowserRouter as Router, Redirect } from 'react-router-dom';
const MainPage = ({ ChildRoutes }) => {
const [foo, setFoo] = useState(0);
const [data, setData] = useState(0);
const incrementFoo = () => setFoo(prev => prev + 1);
useEffect(() =>{
console.log("mount main")
},[]);
useEffect(() =>{
setData(foo * 2)
},[foo]);
return (
<div>
<h1>Main Page</h1>
<p>data: {data}</p>
<button onClick={incrementFoo}>Increment foo {foo}</button>
<ChildRoutes foo={foo} />
</div>
);
};
const SecondPage = ({ ChildRoutes, foo }) => {
const [bar, setBar] = useState(0);
const incrementBar = () => setBar(prev => prev + 1);
useEffect(() =>{
console.log("mount second")
},[]);
return (
<div>
<h2>Second Page</h2>
<button onClick={incrementBar}>Increment bar</button>
<ChildRoutes foo={foo} bar={bar} />
</div>
);
};
const ThirdPage = ({ foo, bar }) => {
useEffect(() =>{
console.log("mount third")
},[]);
return (
<div>
<h3>Third Page</h3>
<p>foo: {foo}</p>
<p>bar: {bar}</p>
</div>
);
};
const routingConfig = [{
path: '/main',
component: MainPage,
routes: [
{
path: '/main/second',
component: SecondPage,
routes: [
{
path: '/main/second/third',
component: ThirdPage
},
]
}
]
}];
const Routing = ({ routes: passedRoutes, ...rest }) => {
if (!passedRoutes) return null;
return (
<Switch>
{passedRoutes.map(({ routes, component: Component, ...route }) => {
return (
<Route key={route.path} {...route}>
<Component {...rest} ChildRoutes={props => <Routing routes={routes} {...props}/>}/>
</Route>
);
})}
</Switch>
);
};
export const App = () => {
return(
<Router>
<Routing routes={routingConfig}/>
<Route exact path="/">
<Redirect to="/main/second/third" />
</Route>
</Router>
)
};
export default App;
Run Code Online (Sandbox Code Playgroud)
MainPage 中的每个单独状态更改都会导致重新安装 ThirdPage。
由于 React Router,我无法使用 StackOverflow 创建代码段。所以这是一个具有完全相同代码的代码和框:https ://codesandbox.io/s/summer-mountain-unpvr?file=/src/App.js
预期行为是每个页面仅触发一次安装。我知道我可以通过使用 Redux 或 React.Context 来解决这个问题,但现在我想知道是什么导致了这种行为,以及是否可以避免。
==========================
更新:使用 React.Context 它可以工作,但我想知道没有它是否可以完成?
工件:
const ChildRouteContext = React.createContext();
const ChildRoutesWrapper = props => {
return (
<ChildRouteContext.Consumer>
{ routes => <Routing routes={routes} {...props} /> }
</ChildRouteContext.Consumer>
);
}
const Routing = ({ routes: passedRoutes, ...rest }) => {
if (!passedRoutes) return null;
return (
<Switch>
{passedRoutes.map(({ routes, component: Component, ...route }) => {
return (
<Route key={route.path} {...route}>
<ChildRouteContext.Provider value={routes}>
<Component {...rest} ChildRoutes={ChildRoutesWrapper}/>
</ChildRouteContext.Provider>
</Route>
);
})}
</Switch>
);
};
Run Code Online (Sandbox Code Playgroud)
要理解这个问题,我认为您可能需要了解 React 组件和 React 元素之间的区别以及 React 协调是如何工作的。
React 组件要么是基于类的组件,要么是基于函数的组件。你可以把它想象成一个接受一些 props 并最终返回一个 React 元素的函数。你应该只创建一次 React 组件。
另一方面,React 元素是一个描述组件实例或 DOM 节点及其所需属性的对象。JSX 提供了通过其 React 组件创建 React 元素的语法:
<Component someProps={...} />
在某个时间点,您的 React 应用程序是一棵 React 元素树。这棵树最终会转换为实际的 DOM 节点,并显示在我们的屏幕上。
每次状态发生变化时,React 都会构建另一棵全新的树。之后,React 需要想办法根据新树和最后一棵树之间的差异来高效更新 DOM 节点。这个过程称为Reconciliation。此过程的差异算法是比较两个根元素时,如果这两个元素是:
// this means re-mount that element (unmount and mount again)。// this means keep the instance (React element) and update the props以上是理论的简要介绍,让我们进入实践。
我打个比方:React 组件是一个工厂,而 React 元素是一个特定工厂的产品。工厂应该创建一次。
这行代码ChildRoutes是一个工厂,每次Component重新渲染的父级都会创建一个新工厂(由于 Javascript 函数的创建方式):
<Component {...rest} ChildRoutes={props => <Routing routes={routes} {...props}/>}/>
Run Code Online (Sandbox Code Playgroud)
基于routingConfig,MainPage创建了一个工厂来创建SecondPage. 在SecondPage创建了一个工厂来创建ThirdPage。在MainPage, 当有状态更新时(例如:foo增加了):
MainPage重新呈现。它使用它的SecondPage工厂来创造一个SecondPage产品。由于它的工厂没有改变,创建的SecondPage产品后来根据“相同类型的组件元素”规则进行了差异化。SecondPage重新呈现(由于foo道具的变化)。它的ThirdPage工厂再次创建。所以新创建的ThirdPage产品与以前的ThirdPage产品不同,后来根据“不同类型的元素”进行了区分。这就是导致ThirdPage元素重新安装的原因。为了解决这个问题,我使用渲染道具作为使用“创建一次”工厂的一种方式,以便其创建的产品稍后通过“相同类型的组件元素”规则进行区分。
<Component
{...rest}
renderChildRoutes={(props) => (<Routing routes={routes} {...props} />)}
/>
Run Code Online (Sandbox Code Playgroud)
这是工作演示:https : //codesandbox.io/s/sad-microservice-k5ny0
参考:
| 归档时间: |
|
| 查看次数: |
829 次 |
| 最近记录: |