【问题标题】:Prevent browser from making the same async calls as the server防止浏览器进行与服务器相同的异步调用
【发布时间】:2018-10-31 14:25:44
【问题描述】:

我正在关注本教程:https://crypt.codemancers.com/posts/2017-06-03-reactjs-server-side-rendering-with-router-v4-and-redux/,我认为这是在 react (?) 中进行服务器端渲染的“标准”方式。

基本上发生的事情是我使用反应路由器(v4)来制作所有即将渲染的组件的树:

const promises = branch.map(({ route }) => {
    return route.component.fetchInitialData
        ? route.component.fetchInitialData(store.dispatch)
        : Promise.resolve();
});

等待所有这些承诺解决,然后致电renderToString

在我的组件中,我有一个名为 fetchInitialData 的静态函数,如下所示:

class Users extends React.Component {
    static fetchInitialData(dispatch) {
        return dispatch(getUsers());
    }
    componentDidMount() {
        this.props.getUsers();
    }
    render() {
        ...
    }
}

export default connect((state) => {
    return { users: state.users };
}, (dispatch) => {
    return bindActionCreators({ getUsers }, dispatch);
})(Users);

除了在服务器和客户端都调用getUsers之外,所有这些都很好。

我当然可以检查是否有任何用户已加载并且不在componentDidMount 中调用getUsers,但必须有更好、更明确的方法来不进行两次异步调用。

【问题讨论】:

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


    【解决方案1】:

    在越来越熟悉 react 之后,我觉得我有一个解决方案。

    我在所有渲染路径上传递了一个browserContext 对象,就像服务器上的staticContext 一样。在browserContext 我设置了两个值; isFirstRenderusingDevServerisFirstRender 仅在应用第一次渲染时为真,usingDevServer 仅在使用 webpack-dev-server 时为真。

    const store = createStore(reducers, initialReduxState, middleware);

    浏览器端的入口文件:

    const browserContext = {
        isFirstRender: true,
        usingDevServer: !!process.env.USING_DEV_SERVER
    };
    
    const BrowserApp = () => {
        return (
            <Provider store={store}>
                <BrowserRouter>
                    {renderRoutes(routes, { store, browserContext })}
                </BrowserRouter>
            </Provider>
        );
    };
    
    hydrate(
        <BrowserApp />,
        document.getElementById('root')
    );
    
    browserContext.isFirstRender = false;
    

    USING_DEV_SERVER 在 webpack 配置文件中使用 webpack.DefinePlugin 定义

    然后我编写了一个 HOC 组件,它使用此信息仅在需要的情况下获取初始数据:

    function wrapInitialDataComponent(Component) {
        class InitialDatacomponent extends React.Component {
            componentDidMount() {
                const { store, browserContext, match } = this.props;
    
                const fetchRequired = browserContext.usingDevServer || !browserContext.isFirstRender;
    
                if (fetchRequired && Component.fetchInitialData) {
                    Component.fetchInitialData(store.dispatch, match);
                }
            }
            render() {
                return <Component {...this.props} />;
            }
        }
    
        // Copy any static methods.
        hoistNonReactStatics(InitialDatacomponent, Component);
    
        // Set display name for debugging.
        InitialDatacomponent.displayName = `InitialDatacomponent(${getDisplayName(Component)})`;
    
        return InitialDatacomponent;
    }
    

    最后要做的就是用这个 HOC 组件包装任何使用 react router 渲染的组件。我通过简单地递归迭代路由来做到这一点:

    function wrapRoutes(routes) {
        routes.forEach((route) => {
            route.component = wrapInitialDataComponent(route.component);
    
            if (route.routes) {
                wrapRoutes(route.routes);
            }
        });
    }
    
    const routes = [ ... ];
    
    wrapRoutes(routes);
    

    这似乎可以解决问题:)

    【讨论】:

      猜你喜欢
      • 2012-07-18
      • 2019-01-16
      • 2019-04-20
      • 1970-01-01
      • 2016-01-21
      • 2011-01-09
      • 2011-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多