简体   繁体   English

使用React Router 4预取数据

[英]Prefetch data with React Router 4

I'm trying to migrate to v4 of React Router and I'm stuck with the prefetch data. 我正在尝试迁移到React Router的v4,并且卡住了预取数据。 The latests versions we used to have match function. 我们过去具有匹配功能的最新版本。

One of the returning parameters was renderProps but now I can't use match so I don't know how to prefetch data. 返回的参数之一是renderProps,但是现在我不能使用match,所以我不知道如何预取数据。 The documentation shows how to load data but doesn't works for me because I'm using the new version of react-router-redux to sync the store. 文档显示了如何加载数据,但不适用于我,因为我正在使用新版本的react-router-redux同步存储。

Here's the code the client: 这是客户端的代码:

return (
  <MuiThemeProvider muiTheme={letgoMuiTheme}>
    <Provider store={store}>
      <IntlProvider>
        { routes(history, store) }
      </IntlProvider>
  </Provider>
</MuiThemeProvider>
);

Routes: 路线:

export default function routes(history) {
  return (
    <ConnectedRouter history={history}>
      <Switch>
        <Route exact path="/" component={App} />
        <Route path="/:lang/chat" component={Chat} />
        ...
        <Route path="*" component={NotFound} status={404} />
      </Switch>
    </ConnectedRouter>
  );
}

Server: 服务器:

if (context.url) {
  res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else {
  const render = () => {
    const body = renderToString(
      <StaticRouter
        location={req.url}
        context={context}
      >
        <MuiThemeProvider muiTheme={muiTheme}>
          <Provider store={store}>
            <IntlProvider>
              { routes(history, store) }
            </IntlProvider>
          </Provider>
        </MuiThemeProvider>,
      </StaticRouter>
    );

    res.setHeader('content-language', locale);
    res.render('index', {
      head,
      body,
      locale,
      state: stringState,
      ...
    });
  };

  Promise.all(
    prefetchData(renderProps, store),
  )
  .then(render)
  .catch(prefetchError => next(prefetchError));
}

That's my old server file: 那是我的旧服务器文件:

    match({ history, routes, location: req.url }, (error, redirectLocation, renderProps) => {
      if (error) {
        throw error;
      } else if (redirectLocation) {
        res.redirect(302, redirectLocation.pathname + redirectLocation.search);
      } else if (renderProps) {
        // Render react components once store is initializaed and return HTTP response
        const render = () => {
          const body = renderToString(
            <MuiThemeProvider muiTheme={muiTheme}>
              <Provider store={store}>
                <IntlProvider>
                  <RouterContext {...renderProps} />
                </IntlProvider>
              </Provider>
            </MuiThemeProvider>,
          );

          res.setHeader('content-language', locale);
          res.render('index', {
            head,
            newrelicScript,
            body,
            locale,
            state: stringState,
            ...
          });
        };

        // Fetch components data and render HTML (no matter the fetch results
        // we need to render something)
        Promise.all(
          prefetchData(renderProps, store),
        )
        .then(render)
        .catch(prefetchError => next(prefetchError));
      } else {
        logger.warning('Ops !!! We should never arrive here :(');
        next();
      }
    });

And prefetch data: 并预取数据:

export const prefetchData = (renderProps, store) => {
  const { components, params } = renderProps;
  const { dispatch } = store;
  const state = store.getState();
  const extractWrappedComponent = (component) => {
    if (component.WrappedComponent) {
      return extractWrappedComponent(component.WrappedComponent);
    }
    return component;
  };

  return components
    .filter(component => component !== undefined)
    // Get component, be aware if they are wrapped by redux connect or intl.
    .map(component => extractWrappedComponent(component))
    // Get the fetchData method of each component
    .map(component => component.fetchData)
    .filter(fetchData => fetchData !== undefined)
    // Invoke each fetchData to initialize the redux state.
    .map(fetchData => fetchData(state, dispatch, params));
};

After looking into it, I found a solution that works for me. 经过研究,我找到了适合我的解决方案。 This is the helper (before was prefetchData). 这是帮助器(之前是prefetchData)。 matchPath is from react-router-dom . matchPath来自react-router-dom

export const getRouteData = (routesArray, url, store) => {
  const { dispatch } = store;
  const state = store.getState();
  const extractWrappedComponent = (component) => {
    if (component.WrappedComponent) {
      return extractWrappedComponent(component.WrappedComponent);
    }
    return component;
  };

  return routesArray.map((route) => {
    const component = route.component;
    const extractedComponent = extractWrappedComponent(component);
    const match = matchPath(url, { path: route.path, exact: route.exact, strict: false });

    if (match) {
      if (extractedComponent.fetchData !== undefined) {
        extractedComponent.fetchData(state, dispatch, match.params);
      }
    }
  });
}

Then I call it: 然后我称之为:

getRouteData(routes, req.url, store);

Were routes is a static routes : 路由是静态路由

export const routes = [
  {
    path: '/',
    component: App
  },
  {
    path: '/:lang/chat',
    component: Chat
  }
  ...

My server file now does not have match. 我的服务器文件现在不匹配。 Instead: 代替:

if (context.url) {
  res.redirect(302, context.url);
} else {
  const render = () => {
    const body = renderApp(req, muiTheme, store, history, context);

    res.render('index', {
      head,
      body,
      locale,
      ...
    });
  };

  Promise.all(
    getRouteData(routes, req.url, store),
  )
  .then(render)
  .catch(prefetchError => next(prefetchError));

RenderApp file (Client has to be the same without StaticRouter ): RenderApp文件(没有StaticRouter客户端必须相同):

const renderApp = (req, muiTheme, store, history, context) => {
  const app = (
    <MuiThemeProvider muiTheme={muiTheme}>
      <Provider store={store}>
        <IntlProvider>
          <StaticRouter
            location={req.url}
            context={context}
          >
            { appRoutes(history) }
          </StaticRouter>
        </IntlProvider>
      </Provider>
    </MuiThemeProvider>
  );

  return renderToString(app);
};

appRoutes file: appRoutes文件:

export default function appRoutes(history) {
  return (
    <ConnectedRouter history={history}>
      <div>
        {routes.map((route, key) => (
          <Route key={key} {...route} />
        ))}
      </div>
    </ConnectedRouter>
  );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM