代码之家  ›  专栏  ›  技术社区  ›  asierguti

将道具添加到react router v4中的包裹路由组件

  •  1
  • asierguti  · 技术社区  · 7 年前

    我有共享相同行为、布局等的路由。我想将布局中的道具传递给路由中的组件(仪表板和登录)

    我的路线。js文件如下

    //imports omited    
    export default (
        <AppLayout>
            <Route component={Dashboard} path="/" key="/" />
            <Route component={Login} path="/login" key="/login" />
        </AppLayout>
    );
    

    AppLayout的渲染方法。js有以下代码

    const childrenWithExtraProp = React.Children.map(this.props.children, (child) => {
                return React.cloneElement(child, {
                    component: React.cloneElement(child.props.component, {
                        functions: {
                            updateMenuTitle: this.updateTitle //function
                        }
                    })
                });
            });
    

    此代码导致许多错误:

    Warning: Failed prop type: Invalid prop `component` of type `object` supplied to `Route`, expected `function`.
        in Route
        in AppHeader
        in Router (created by BrowserRouter)
        in BrowserRouter (created by App)
        in App
    
    
    Check the render method of `Route`.
        in Route
        in AppHeader
        in Router (created by BrowserRouter)
        in BrowserRouter (created by App)
        in App
    
    
    Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
    
        Check the render method of `Route`.
            at invariant (invariant.js?7313:42)
            at createFiberFromElement (react-dom.development.js?cada:5753)
            at reconcileSingleElement (react-dom.development.js?cada:7531)
            at reconcileChildFibers (react-dom.development.js?cada:7635)
            at reconcileChildrenAtExpirationTime (react-dom.development.js?cada:7756)
            at reconcileChildren (react-dom.development.js?cada:7747)
            at finishClassComponent (react-dom.development.js?cada:7881)
            at updateClassComponent (react-dom.development.js?cada:7850)
            at beginWork (react-dom.development.js?cada:8225)
            at performUnitOfWork (react-dom.development.js?cada:10224)
    

    在上一个项目中,我曾在Route中使用路由,但在React Router v4中,这是不允许的。

    编辑:之前是这样的:

    //Array of routes declared before
    export default (
        <Router history={browserHistory}>
            <Route path="/" component={General}>
                <IndexRoute component={Index} />
                {routes}
            </Route>
        </Router>
    );
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   Andy Ray    7 年前

    我怀疑这就是问题所在:

    component: React.cloneElement(child.props.component, {
    

    child.props.component 不是渲染组件(如 <Dashbard /> ),它是 组件类 (如 Dashboard ). cloneElement 需要渲染组件。并且不能显式地将道具额外传递到 组件类 .

    有几种方法可以实现你正在做的事情。克隆路线对我来说很“棘手”。

    选项1:高阶组件 updateTitle 思维方式

    我会亲自尝试 高阶分量 (一个接受组件类并返回组件类的函数),用于添加此道具,并导出包装在其中的仪表板/登录组件。稍微详细一点,但没有那么棘手:

    临时文件:

    const WithExtraProp = (ContentComponent) => {
        return WithPropWrapper extends Component {
    
            updateMenuTitle() {...}
    
            render() {
                // Add extra props here
                return <ContentComponent {...this.props} functions={{ updateMenuTitle: this.updateMenuTitle }}/>
            }
    
        }
    }
    export default WithExtraProp;
    

    和在仪表板中

    class Dashboard extends Component {...}
    export default WithExtraProp(Dashboard);
    

    使用这种方法,您也可以这样做(尽管我不太喜欢)

    <AppLayout>
        <Route component={WithExtraProp(Dashboard)} path="/" key="/" />
        <Route component={WithExtraProp(Login)} path="/login" key="/login" />
    </AppLayout>
    

    选项2:使用 <Route render={} /> 而不是 component={} 添加道具

    如果您想保持当前设置,您可以在其中隐式/神奇地添加道具 ,我看不到不使用路线的方法 render 道具代替 component . 这样,您可以正常渲染组件并传入道具。

    您可以保持不变:

    <Route component={Dashboard} path="/" key="/" />
    

    像这样:

    const childrenWithExtraProp = React.Children.map(this.props.children, (child) => {
        // Clone the <Route />, remove the `component` prop, add `render` prop
        return React.cloneElement(child, {
    
            // Remove the `component` prop from the Route, since you can't use
            // `component` and `render` on a Route together. This way component
            // just becomes an API for this withExtraPropClass to use to find
            // the right component
            component: null,
    
            render = () => {
                const ChildComponent = child.props.component;
                const functions = {
                    updateMenuTitle: this.updateTitle //function
                };
                return <ChildComponent functions={functions} />;
            }
            })
        });
    });
    

    选项3:制造 AppLayout 高阶组件

    这与选项1相同,最终您可以执行以下操作:

    //imports omited
    export default (
        <Route component={Dashboard} path="/" key="/" />
        <Route component={Login} path="/login" key="/login" />
    );
    

    应用布局 是添加道具的高阶组件。

    const AppLayout = (ContentComponent) => {
        return WithPropWrapper extends Component {
    
            updateMenuTitle() {...}
    
            render() {
                // Add extra props here
                return (
                    <MyLayoutStuff>
                        <ContentComponent {...this.props} functions={{ updateMenuTitle: this.updateMenuTitle }}/>
                    </MyLayoutStuff>
                );
            }
    
        }
    }
    export default AppLayout;
    

    并导出封装在布局中的组件:

    class Dashboard extends Component {...}
    export default AppLayout(Dashboard);
    

    其他想法

    我个人一直在使用与#3最接近的东西。具体来说,我有一个文件 dashboard/Dashboard.js ,并在同一文件夹中, dashboard/index.js ,导出包装在布局中的仪表板。您可以在以下位置看到该模式的示例 this React boilerplate Github folder .

    还有其他选择。你可以 <AppRoutes children=[{component: Dashboard, path="/"}, {...}] /> 不必处理克隆的组件。如果你需要和孩子一起做事情 <render> 我通常更喜欢将它们作为数组传递,而不是子组件和映射。