首页 文章

如何在不经常重新安装的情况下将包装好的组件传递到React Router

提问于
浏览
2

我们正在升级我们的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上的this thread,组件将重新安装,如果:

在渲染过程中调用withRouter(..),每次将新函数传递给每个渲染的Route.component时都会创建一个新的组件类,例如:使用匿名函数} />,它也会创建一个新组件

如果我直接传递 FlagForm 组件问题是固定的,但是我无法利用 preload 函数 .

那么,如何在没有组件重新安装的情况下实现相同的结果呢?

在此先感谢您的帮助!

2 回答

  • 0

    Route 在每次更新时安装新组件的原因是每次都通过 preload 为其分配了一个新类 .

    实际上,每次调用 preload 总是会返回一个 distinct 匿名类,即使使用相同的参数调用时也是如此:

    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;
    }
    

    希望有所帮助!

  • 1

    如果像我一样你没有阅读说明书,答案就在于 <Route> 组件的 render 道具

    https://reacttraining.com/react-router/web/api/Route/render-func

    render:func这允许方便的内联呈现和包装,而不会出现上面所述的不希望的重新安装 .

    因此,您必须使用 render prop,而不是将包装函数传递给 component prop . 但是,你可以完全理解发生了什么,但为了确保params正确传递,这是我的解决方案 .

    我的Preload包装器函数现在是一个呈现Route的React组件...

    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 组件的相反问题,所以这解决了这个问题 .

    希望这能为很多人节省大量时间,因为我已经花了好几天才使这个工作正常 . 这就是我猜的成本!

相关问题