React-Router / Redux - 动态同构路由

时间:2016-07-20 18:26:47

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

  

我正在使用github   erikras/react-redux-universal-hot-example存储库。   
我试图使路线充满活力,以恰当地反映现实世界的情景。

  1. client / entry.js 页面调用传递商店参数的共享模块函数'getRoutes'。

    const component = (
      <Router render={(props) =>
                <ReduxAsyncConnect {...props} helpers={{client}} 
                   filter={item => !item.deferred} />
             } history={history}>
        // where the routes are populated
        {getRoutes(store)} // <-- function call
      </Router>
    

    );

  2. 服务器的 isomorphic-routes.config.server.js 还调用getRoutes(store)以与客户端进行路由匹配:

        match({history, routes: getRoutes(store), location: req.originalUrl},    
        (error, redirectLocation, renderProps) => {
         .....
      });
    
  3. 共享 dynamic-routes.shared.js 页面是一个reg JavaScript函数'getRoutes':

     export default (store) => {
        ......
    
      let dynRoutes= [];
      store.dispatch(loadNav()).then(result => {
      dynRoutes = result;
      })
    
    return (
     <Route path="/" component={App}>
       { /* Home (main) route */ }
       <IndexRoute component={Home}/>
        { /* Get dynamic routes from database */ }
        { dynRoutes.length > 0 ?
         <DynamicRoutes dynRoutes={dynRoutes} />
         : ''}    
        { /* Catch all route */ }
        <Route path="*" component={NotFound} status={404} />
       </Route>
       )
      };
    
  4. 在第二个'四处走动'时,dynRoutes'数组确实接收到路由集合,return语句不会重新处理  确定组件的长度为零,并忽略回调值。

    我知道这不是一个React组件,它提供了使用新返回值重新加载的好处,但是,是否有人有任何见解我如何使用回调值返回此JavaScript函数?

    谢谢。

2 个答案:

答案 0 :(得分:1)

  

简短的回答是我不仅需要做出决定    dynamic-routes.shared.js 异步,以及任何其他功能   来自服务器&amp;客户端即可。

我用过   关于 dynamic-routes.shared.js 承诺,以及 async / await   服务器/客户端调用到dynamic-routes.shared.js。

  我没有把数据扔进JSON,你可以自己解决这个问题。

<强> 1。动态routes.shared.js

      function routesWithStore(store) {
       return new Promise(function(resolve, reject) {
       // you can use something like this to actually have these
       // routes in a database
       // let dynRoutes= [];
       // store.dispatch(loadNav()).then(result => {
       // dynRoutes = result;
       // }) 
       // resolve(dynRoutes.map(route => {
       //   ..... your code here .....
       // }))
         resolve(
           {
             path: '',
             component: App,
             childRoutes: [
               {path: '/', component: Home},
               {path: 'home', component: Home},
               {path: 'about', component: About},
               {path: '*', component: NotFound}
             ]
           }
         )
       });
     }

       function getRoutes(store) {      
        return(
          routesWithStore(store)
         )
       }

     exports.getRoutes = getRoutes;

<强> 2.client / entry.js

    // async call to dynamic-routes.shared.js ////////
    async function main() {
      try {
        const result = await getRoutes(store);
        processRoutes(result);
      } catch(err) {
        console.log(err.message)
      }
    }

    function processRoutes(result) {
      const component = (
        <Router render={(props) =>
            <ReduxAsyncConnect {...props} helpers={{client}} 
              filter={item => !item.deferred} />
                } history={history}>
                {result} <------------- client value from dynamic-routes.shared.js
              </Router>
            );

      ReactDOM.render(
        <Provider store={store} key="provider">
          {component}
        </Provider>,
         document.querySelector('#root');
         );

_

  if (process.env.NODE_ENV !== 'production') {
        window.React = React; // enable debugger

      if (!dest || !dest.firstChild 
                || !dest.firstChild.attributes 
                || !dest.firstChild.attributes['data-react-checksum']) {
      console.error
       ('Server-side React render was discarded. ' + 
        'Make sure that your initial render does not contain any client-side code.');
       }
     }

      if (__DEVTOOLS__ && !window.devToolsExtension) {
       const DevTools = require('shared/redux/dev-tools/dev-tools.redux.shared');
       ReactDOM.render(
        <Provider store={store} key="provider">
           <div>
            {component}
            <DevTools />
           </div>
        </Provider>,
         document.querySelector('#root');
        );
      }
    }

   main();

第3。同构-routes.config.server

     module.exports = (app) => {
      app.use((req, res) => {
        if (__DEVELOPMENT__) {
          // Do not cache webpack stats: the script file would change since
          // hot module replacement is enabled in the development env
          webpackIsomorphicTools.refresh();
        }
        const client = new ApiClient(req);
        const memoryHistory = createHistory(req.originalUrl);
        const store = createStore(memoryHistory, client);
        const history = syncHistoryWithStore(memoryHistory, store);

        function hydrateOnClient() {
          res.send('<!doctype html>\n' +
            ReactDOM.renderToString(

              <Html assets={webpackIsomorphicTools.assets()} 
                   store={store}/>));
        }

        if (__DISABLE_SSR__) {
          hydrateOnClient();
          return;
        }

_

        // Async call to dynamic-routes.shared.js ////////

        async function main() {
          try {
            const routesResult = await getRoutes(store);
            // pass routesResult below
            match({history, routes: routesResult, location: req.originalUrl}, 
                 (error, redirectLocation, renderProps) => {
              if (redirectLocation) {
                res.redirect(redirectLocation.pathname + redirectLocation.search);
              } else if (error) {
                console.error('ROUTER ERROR:', pretty.render(error));
                res.status(500);
                hydrateOnClient();

_

              } else if (renderProps) {
                loadOnServer({...renderProps, store, helpers: {client}}).then(() => {
                  const component = (
                    <Provider store={store} key="provider">
                      <ReduxAsyncConnect {...renderProps} />
                    </Provider>
                  );

                  res.status(200);

                  global.navigator = {userAgent: req.headers['user-agent']};

                  res.send('<!doctype html>\n' +
                    ReactDOM.renderToString(
                         <Html assets={webpackIsomorphicTools.assets()}   
                          component={component} store={store}/>));
                });
              } else {
                res.status(404).send('Iso Routes Not Found ' + routeResult);
              }
            });

          } catch(error) {
            console.error(error);
          }
        }

        main();

      });
    };

我希望这有助于任何想要使其同构路线动态化的人。
让美国再次伟大!

答案 1 :(得分:0)

标准解决方案是将getRoutes设置为遵循React组件生命周期的真实React组件,在这种情况下,当回调返回时可以使用this.setState({ dynRoutes })来触发重新呈现。在其当前设置中,getRoutes将永远无法强制调用者/父级重新呈现。

您需要将store.dispatch(loadNav())调用与组件呈现分开,并构造代码,以便promise处理程序在setState层次结构中的组件内部触发重新呈现,或再次调用根ReactDOM.render