在React中,如何检测我的组件是从客户端还是服务器呈现?

时间:2015-08-26 00:59:48

标签: javascript node.js reactjs

我正在构建一个同构应用程序,但我使用的是仅在客户端呈现的第三方组件。所以,特别是对于这个组件,我只需要在客户端渲染时才渲染它。

如何检测我是在客户端还是在服务器上?我正在寻找isClient()isServer()等内容。

9 个答案:

答案 0 :(得分:29)

在内部,React使用名为ExecutionEnvironment的实用程序。它实现了一些有用的属性,如canUseDOMcanUseEventListeners。该解决方案基本上只是建议here

canUseDOM

的实施
var canUseDOM = !!(
  (typeof window !== 'undefined' &&
  window.document && window.document.createElement)
);

我在我的应用程序中使用这个

var ExecutionEnvironment = require('react/node_modules/fbjs/lib/ExecutionEnvironment');
...
render() {
  <div>{ ExecutionEnvironment.canUseDOM ? this.renderMyComponent() : null }</div>
}

编辑这是一个无法记录的功能,不应直接使用。它的位置可能会因版本而异。我分享这个作为一种说法&#34;这是你能做的最好的事情&#34;通过展示Facebook团队内部使用的内容。您可能希望将此代码(它很小)复制到您自己的项目中,因此您不必担心跟踪其版本之间的位置或潜在的重大更改。

另一个编辑有人为此代码创建了npm package。我建议使用它。

npm install exenv --save

答案 1 :(得分:8)

可能相关的两件事:

许多项目使用一些约定来设置全局SERVER或CLIENT布尔值,因此所有代码都可以根据它进行切换。在您的服务器包中,设置一些全局的like in this project

global.__SERVER__ = true;

在您的客户端捆绑包中,将一些全局客户端设置为true,您可以通过一种方式实现with Webpack's DefinePlugin

new webpack.DefinePlugin({
  __CLIENT__: true
})

使用上述方法,您可以在willMount或render中基于该变量进行切换,以在服务器上执行一项操作,在客户端上执行另一项操作。

这里可能有用的第二件事是componentDidMount仅在客户端上运行,但不在服务器上运行。

答案 2 :(得分:2)

您也可以使用componentDidMount(),因为当页面是服务器端呈现时,不会运行此生命周期方法。

答案 3 :(得分:1)

您可以使用反应lifecyle事件(例如:componentDidMount)来检测服务器/客户端渲染。

示例

像钩子一样

import { useState, useEffect } from 'react'

function useIsServer () {
  const [isServer, setIsServer] = useState(true)
  useEffect(() => {
    setIsServer(false)
  }, [])
  return isServer
}

用法

请参阅下文(功能组件)

作为功能组件

import useIsServer from './above'

function ServerOnly ({ children = null, onClient = null }) {
  const isServer = useIsServer()
  return isServer
    ? children
    : onClient
}

用法

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>

作为类组件

class ServerOnly extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      isServer: true
    }
  }

  componentDidMount() {
    this.setState({
      isServer: false
    })
  }

  render () {
    const { isServer } = this.state
    const { children, onClient } = this.props
    return isServer
      ? children
      : onClient
  }
}

用法

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>

答案 4 :(得分:0)

在服务器元素层次结构的最顶层,可以添加ServerContext,如下所示:

class ServerContext extends React.Component {
  getChildContext() { return { isServer: true }; }
  render() { return React.Children.only(this.props.children); }
}

ServerContext.propTypes = {
  children: React.PropTypes.node.isRequired,
};

ServerContext.childContextTypes = {
  isServer: React.PropTypes.bool.isRequired,
};

// Create our React application element.
const reactAppElement = (
  <ServerContext>
    <CodeSplitProvider context={codeSplitContext}>
      <ServerRouter location={request.url} context={reactRouterContext}>
        <DemoApp />
      </ServerRouter>
    </CodeSplitProvider>
  </ServerContext>
);

这样做,应该可以从上下文中读取isServer:

const Layout = (_, { isServer }) => (
  // render stuff here
);

答案 5 :(得分:0)

您可以在exenv软件包的帮助下创建一个有用的实用程序。

import { canUseDOM } from 'exenv';

export function onClient(fn: (..._args: any[]) => any): (..._args: any[]) => any {
    if (canUseDOM) {
        return fn;
    }

    if (process.env.NODE_ENV === 'development') {
        console.log(`Called ${fn.name} on client side only`);
    }

    return (): void => {};
}

并像这样使用它

function my_function_for_browser_only(arg1: number, arg2: string) {}

onClient(my_function_for_browser_only)(123, "Hi !");

该函数只会在客户端调用,如果设置NODE_ENV=development

,它将登录服务器端该函数已在客户端调用

(这是打字稿,请删除JS的类型:))

答案 6 :(得分:0)

您也可以只使用use-ssr React钩子

import useSSR from 'use-ssr'

const App = () => {
  var { isBrowser, isServer } = useSSR()

  // Want array destructuring? You can do that too!
  var [isBrowser, isServer] = useSSR()

  /*
   * In your browser's chrome devtools console you should see
   * > IS BROWSER: 
   * > IS SERVER: 
   *
   * AND, in your terminal where your server is running you should see
   * > IS BROWSER: 
   * > IS SERVER: 
   */
  console.log('IS BROWSER: ', isBrowser ? '' : '')
  console.log('IS SERVER: ', isServer ? '' : '')
  return (
    <>
      Is in browser? {isBrowser ? '' : ''}
      <br />
      Is on server? {isServer ? '' : ''}
    </>
  )
}

Example

答案 7 :(得分:0)

您可以检查是否定义了全局window变量。 就像在浏览器中一样,应始终对其进行定义。

var isBrowser = window!==undefined

答案 8 :(得分:0)

if (typeof window === "undefined") { //client side code }

没有typeof,您会得到一个错误。