【问题标题】:How to pass a wrapped component into React Router without it constantly remounting如何在不不断重新安装的情况下将包装的组件传递给 React Router
【发布时间】:2018-04-13 00:08:23
【问题描述】:

我们正在升级我们的 React 应用程序,经过数小时的痛苦之后,我们意识到将包装好的组件传递到 React Router(V4 和可能其他)会导致组件在每次新道具时“重新安装”进来了。

这是包装好的组件...

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 功能。

那么,如何在不重新安装组件的情况下获得相同的结果?

提前感谢您的帮助!

【问题讨论】:

  • 您能解释一下预加载应该如何工作吗?它是否应该在收到的第一个 props 中只调用一次数据?或者它也应该对道具变化做出反应?预加载所需的道具从何而来?

标签: javascript reactjs redux react-router react-router-v4


【解决方案1】:

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

确实,每次调用preload 总是返回一个不同的 匿名类,甚至 当使用相同的参数调用时:

console.log( preload(FlagForm,props) != preload(FlagForm,props) ) // true

因此,由于问题是 preloadFlagsApp 组件的 render 方法中被调用,因此首先将其移出该范围:

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

希望有帮助!

【讨论】:

    【解决方案2】:

    如果你和我一样没看说明,答案就在&lt;Route&gt; 组件的render 属性中

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

    渲染:函数

    这允许方便的内联渲染和包装,而不需要上面解释的不希望的重新安装。

    因此,您必须使用render 属性,而不是将包装函数传递给component 属性。但是,您不能像我在上面所做的那样传入一个包装好的组件。我仍然不完全理解发生了什么,但为了确保正确传递参数,这是我的解决方案。

    我的 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 组件没有重新安装的相反问题,所以这解决了这个问题。

    希望这可以为很多人节省大量时间,因为我确实花了好几天时间才让这项工作正常进行。我猜这就是成为前沿技术的代价!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-24
      • 2020-06-14
      • 2018-06-03
      • 2023-04-11
      • 2020-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多