[英]How to pass a wrapped component into React Router without it constantly remounting
我們正在升級我們的 React 應用程序,經過數小時的痛苦之后,我們意識到將包裝的組件傳遞到 React Router(V4 或其他)會導致組件在每次傳入新 prop 時“重新安裝”。
這是包裝的組件...
export default function preload(WrappedComponent, props) {
class Preload extends React.Component {
componentWillMount() {
getDataForComponent(props);
}
render() {
return <WrappedComponent {...props} />;
}
}
return Preload;
}
這是我們如何使用它...
const FlagsApp = (props) => {
return (
<Route path="/report/:reportId/flag/:id/edit" component{preload(FlagForm, props)} />
);
};
每當我們分派一個動作然后接收更新時,組件就會重新掛載,從而導致很多問題。 根據github上的這個線程,如果出現以下情況,組件將重新安裝:
- 你在渲染過程中調用 withRouter(..) 每次都會創建一個新的組件類
- 您每次渲染都會向 Route.component 傳遞一個新函數,例如使用匿名函數 {...}} />,這也會創建一個新組件
如果我直接傳遞FlagForm
組件,問題就解決了,但是我無法利用preload
功能。
那么,如何在不重新安裝組件的情況下實現相同的結果?
在此先感謝您的幫助!
Route
在每次更新時都安裝一個新組件的原因是每次都通過preload
為其分配了一個新類。
事實上,每次調用preload
總是返回一個不同的匿名類,即使調用時使用相同的參數:
console.log( preload(FlagForm,props) != preload(FlagForm,props) ) // true
因此,由於問題是在FlagsApp
組件的render
方法中調用了preload
,因此首先將其移到該范圍之外:
const PreloadedFlagForm = preload(FlagForm, props) //moved out
const FlagsApp = (props) => {
return (
<Route path="/report/:reportId/flag/:id/edit"
component={PreloadedFlagForm} /> //assign component directly
);
};
這樣Route
的組件不會在更新之間改變。
現在關於preload
揮之不去的props
論點:這實際上是一種反模式。 傳遞props
的正確方法就是傳遞任何組件的標准方式:
const PreloadedFlagForm = preload(FlagForm) //drop the props arg
const FlagsApp = (props) => {
return (
<Route path="/report/:reportId/flag/:id/edit"
component={<PreloadedFlagForm {...props} />} //spread it in here instead
/>
);
};
所以preload
的代碼變成了:
export default function preload(WrappedComponent) {
class Preload extends React.Component {
componentWillMount() {
getDataForComponent(this.props);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return Preload;
}
希望有幫助!
如果像我一樣沒有閱讀說明,答案就在<Route>
組件的render
屬性中
https://reacttraining.com/react-router/web/api/Route/render-func
渲染:函數
這允許方便的內聯渲染和包裝,而無需上面解釋的不需要的重新安裝。
因此,您必須使用render
道具,而不是將包裝函數傳遞到component
道具中。 但是,您不能像我上面所做的那樣傳入包裝的組件。 我仍然不完全理解發生了什么,但為了確保正確傳遞參數,這是我的解決方案。
我的 Preload 包裝器函數現在是一個 React 組件,它呈現一個 Route...
export default class PreloadRoute extends React.Component {
static propTypes = {
preload: PropTypes.func.isRequired,
data: PropTypes.shape().isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}),
}
componentWillMount() {
this.props.preload(this.props.data);
}
componentWillReceiveProps({ location = {}, preload, data }) {
const { location: prevLocation = {} } = this.props;
if (prevLocation.pathname !== location.pathname) {
preload(data);
}
}
render() {
return (
<Route {...this.props} />
);
}
}
然后我像這樣使用它......
const FlagsApp = (props) => {
return (
<Switch>
<PreloadRoute exact path="/report/:reportId/flag/new" preload={showNewFlagForm} data={props} render={() => <FlagForm />} />
<PreloadRoute exact path="/report/:reportId/flag/:id" preload={showFlag} data={props} render={() => <ViewFlag />} />
<PreloadRoute path="/report/:reportId/flag/:id/edit" preload={showEditFlagForm} data={props} render={() => <FlagForm />} />
</Switch>
);
};
我在componentWillMount
和componentWillReceiveProps
都調用this.props.preload
的原因是因為我在導航時遇到了PreloadRoute
組件沒有重新安裝的相反問題,所以這解決了這個問題。
希望這可以為很多人節省大量時間,因為我確實花了幾天時間才使這項工作恰到好處。 我猜這就是流血邊緣的代價!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.