简体   繁体   中英

Is there a cleaner way of getting current URL in both client and server side in isomorphic react apps?

I am developing an app based in this React Redux boilerplate. In one component I need to get the current URL when component is mounted in order to generate a shareable URL for social media. Than component is being accesible from dynamically generated URLs with React Router. In client side I wouldn't have any problem by getting it through javascript document object but the problem lies server side.

I thought about making available in the Redux store the Node.js environment data kept in this config.js file, where hostname is kept

// Node.js environment file config.js    
require('babel-polyfill');

const environment = { 
  development: {
    isProduction: false
  },  
  production: {
    isProduction: true
  }
}[process.env.NODE_ENV || 'development'];

module.exports = Object.assign({
  host: process.env.HOST || 'localhost',
  port: process.env.PORT,
  apiHost: process.env.APIHOST || 'localhost',
  apiPort: process.env.APIPORT
}, environment);

and with the location object of React Router set as props in the component get the pathname and fully construct the URL.

What I have done is to create a simple reducer with an initial state to keep the Node.js config enviroment data and just the default action to include in my combineReducers function with the other app reducers.

const initialState = { 
  config: {}
};

export default function config(state = initialState, action = {}) {
  switch (action.type) {
    default:
      return state;
  }
}

And then I injected the Node.js object with the environment data in the creation of the store, as initial state data, in my server.js file.

import config from './config';
...
const store = createStore(memoryHistory, client, {config});
...

Thus I have available the hostname and I am able in the component to construct its full url both client and server side through Redux store.

But, is there any simpler / cleanest way? I am afraid I might be overkilling it or creating any security issue to my app.

I was having this issue when attempting to use Helmet server-side. I passed in the request as a prop to my main <App> component and then use either that request object (server-side) or the window object (client-side).

App.js

render() {

    // Get full url from client or server
    let fullURL = ''
    if(typeof window !== 'undefined'){
        fullURL = window.location.protocol + '//' + window.location.host + this.props.location.pathname
    }else if(this.props.serverRequest){
        fullURL = this.props.serverRequest.headers.host + '/' + this.props.location.pathname
    }

    const urlTag = fullURL === '' ? null : <meta property="og:url" content={fullURL} />
    const linkTag = fullURL === '' ? null : <link rel="canonical" href={fullURL} />

    const helmet = this.props.siteInfo.site
        ? <Helmet>
            <title>{this.props.siteInfo.site.title}</title>
            <meta property="twitter:title" content={this.props.siteInfo.site.title} />
            {urlTag}
            {linkTag}
            <meta property="og:type" content="website" />
          </Helmet>
        : null

    return (
        <div>
            {helmet}
            <Notices />
            <div className="body">
                <Header />
                <Body />
            </div>
            <Footer />
        </div>
    )
}

server.js

const markup = renderToString(
    <Provider store={store}>
        <StaticRouter location={req.url} context={context}>
            <App serverRequest={req} />
        </StaticRouter>
    </Provider>
)

const helmet = Helmet.renderStatic()

Is this "proper" or "best practice"? No idea, but it works well and is easy to follow :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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