簡體   English   中英

如何在不不斷重新安裝的情況下將包裝的組件傳遞到 React Router

[英]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>
    );
};

我在componentWillMountcomponentWillReceiveProps都調用this.props.preload的原因是因為我在導航時遇到了PreloadRoute組件沒有重新安裝的相反問題,所以這解決了這個問題。

希望這可以為很多人節省大量時間,因為我確實花了幾天時間才使這項工作恰到好處。 我猜這就是流血邊緣的代價!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM