0

I use react-redux + thunk + redial and generate my HTML on server side. I want to generate meta tags using user data coming from another server by react-helmet. in my server:

import { trigger } from 'redial';
trigger('fetch', components, locals)
      .then(() => {
          const state = getState();
          var html = ReactDOMServer.renderToString (
              <Provider store={store} key="provider">
                  <ReactRouter.RouterContext {...renderProps} />
              </Provider>
          );
let head = Helmet.rewind();
response.send(`<!DOCTYPE html>
              <head>
                <meta charSet="UTF-8"></meta>
                <meta httpEquiv="X-UA-Compatible" content="IE=edge,chrome=1"></meta>
                <meta name="viewport" content="width=device-width, initial-scale=1"></meta>
                <link rel='stylesheet' href='/css/bootstrap.css'/>
                ${style? "<style>"+style+"</style>" : "<link rel='stylesheet' href='/css/app.css'/>"}
                    ${head.title}
                    ${head.meta}
                    ${head.link}
                    ${head.script}
                </head>
                <body>
                  <div id="app">${html}</div>
                  <script src='/javascript/bundle.js'></script>
                     ${popUp}
                </body>
              </html>`);

In my component I call dispatch in render() function to run a function in actions file which calls an API and returns user data. No matter where I use dispatch, it has the same result. In the best condition React returns the request and waits for the data coming from the other server. But it returns nothing due to condition I set to check received data from server.

My component

    var profile=[], shouldRender = false
export var Profile = React.createClass({
render () {
  var {x,y,z} = this.props;
  var pathArray = (this.props.path).split( '/' );
  if(!canUseDOM && shouldRender == false)dispatch(
    actions.theProfile(x,y,z)
  ).then((es) => {profile = es; shouldRender= true;Promise.resolve()})
  .catch((et) => console.log("error"));

if(shouldRender){
  console.log("got inside");
  return (
    <div  className="container">
      <RightSideBar page="profile"/>
      <Helmet
            title={metaData('title', name, profileId)}/>
      <div className="col-md-6 col-xs-12 col-md-offset-3 content right">
        <div className="row">
          {profileContent(ideaTypes)}
        </div>
      </div>
      <div className="col-md-3 col-xs-12 sidebar-left hidden-xs hidden-sm hidden-md">
        <LeftSideBar page="profile"/>
      </div>
    </div>
  )
 }
});

and my action which calls API using axios

    export var viewingProfile = (ux,y,z) => {
    return function (dispatch, getState)  {
      return axios.post(root + '/api/getUser', {
          Id: x,
          orgid: y,
          profileId: z
      })
      .then(function (res) {
        if(res.data.statusEntity.statusCode == -201){
          window.location.href = "/user-not-found"
        } else {
          dispatch(updateState(res.data))
          return res.data;

        }
      })
    }
  }
alireza
  • 398
  • 3
  • 12

2 Answers2

0

Calling dispatch in render() method is a wrong practice, since render method is supposed to be immutable. Wrap your component with hooks provided by redial to resolve data apis. Refer example usage in redial repo.

Abhishek Nair
  • 167
  • 2
  • 7
  • I should do it, I noticed it after writing this question but I have not tested it. I'll do it tomorrow and I hope it solves the issue – alireza Feb 14 '17 at 19:51
  • I did it but the issue remains. it is not even calling the action. – alireza Feb 15 '17 at 12:20
0

Finally after a lot of work on rendering data coming from another server in react server-side rendering, I found the best solution. First, you don't need redial, that's redundant, at least in my case. So I removed it from my source. then, I moved

dispatch(
 actions.theProfile(x,y,z)
)

to my routes

export default (store) => {
 const requireApiCall= (nextState, replace, cb) => {
  var state = store.getState();
    return store.dispatch(actions.theProfile(x,y,z)).
    then(()=>{
      cb()
    })

 };
 <Route path="/" component={App}>
   <IndexRoute  component={Home}/>
   <Route path="profile" component={Profile } onEnter={requireApiCall}>
   </Route>
 </Route>
}

The dispatch will execute and the routes wait for call back "cb()" and when my API call is answered, my store gets update with received data. Then action resolves the promise and then the promise inside requireApiCall resolves and returns the call back "cb()". routes begin rendering components and the answer sent to the client will be generated using received data.

I know it's a little complicated but works perfectly.

alireza
  • 398
  • 3
  • 12