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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32216383/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:45:45  来源:igfitidea点击:

In React, how do I detect if my component is rendering from the client or the server?

javascriptnode.jsreactjs

提问by André Pena

I'm building a isomorphic application, but I'm using a third-party component that only renders on the client. So, particularly for this component, I need to only render it when I'm rendering in the client.

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

How do I detect if I'm at the client or at the server? I'm looking for something like isClient()or isServer().

如何检测我是在客户端还是在服务器?我正在寻找类似isClient()或的东西isServer()

回答by Charlie Martin

Internally, React uses a utility called ExecutionEnvironmentfor this. It implements a few useful properties like canUseDOMand canUseEventListeners. The solution is essentially just what's suggested herethough.

在内部,React 使用了一个ExecutionEnvironment为此调用的实用程序。它实现了一些有用的属性,例如canUseDOMcanUseEventListeners。解决方案基本上就是这里所建议的。

The implementation of canUseDOM

实施 canUseDOM

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

I use this in my application like this

我像这样在我的应用程序中使用它

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

EDITThis is an undocumented feature that shouldn't be used directly. Its location will likely change from version to version. I shared this as a way of saying "this is the best you can do" by showing what the Facebook team uses internally. You may want to copy this code (it's tiny) into your own project, so you don't have to worry about keeping up with its location from version to version or potential breaking changes.

编辑这是一个不应直接使用的未记录功能。它的位置可能会因版本而异。我通过展示 Facebook 团队内部使用的内容来分享这一点,以此表达“这是你能做的最好的事情”。您可能希望将此代码(它很小)复制到您自己的项目中,这样您就不必担心在不同版本之间保持其位置或潜在的破坏性更改。

ANOTHER EDITSomeone created an npm packagefor this code. I suggest using that.

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

npm install exenv --save

回答by Andy Ray

Two things that may be relevant:

可能相关的两件事:

Many projects use some convention where they set a global SERVER or CLIENT boolean so all your code can switch based off it. In your server bundle, set some global, like in this project

许多项目使用一些约定来设置全局 SERVER 或 CLIENT 布尔值,因此您的所有代码都可以基于它进行切换。在你的服务器包中,设置一些全局,就像在这个项目中一样

global.__SERVER__ = true;

And in your client bundle, set some global client to true, which you can achieve one way with Webpack's DefinePlugin

并在您的客户端包中,将一些全局客户端设置为 true,您可以使用 Webpack 的 DefinePlugin 的一种方式来实现

new webpack.DefinePlugin({
  __CLIENT__: true
})

With the above approach, you could switch based off that variable in willMount, or render, to do one thing on the server, and another on the client.

使用上述方法,您可以在 willMount 或 render 中关闭该变量,以便在服务器上做一件事,在客户端做另一件事。

The second thing that may be helpful here is componentDidMountonly runs on the client, but not on the server.

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

回答by HaNdTriX

You can use reacts lifecyleevents (e.g.: componentDidMount) to detect server/client side rendering.

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

Examples

例子

As Hook

作为钩

import { useState, useEffect } from 'react'

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

Usage

用法

See below (Functional Component)

见下文(功能组件)

As Functional Component

作为功​​能组件

import useIsServer from './above'

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

Usage

用法

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

As Class Component

作为类组件

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
  }
}

Usage

用法

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

回答by JoeTidee

You can also use componentDidMount(), as this lifecycle method is not run when the page is server-side rendered.

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

回答by Alex Cory

You could also just use the use-ssrReact hook

你也可以只使用use-ssrReact 钩子

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

例子

回答by Ramesh Pareek

You can check if global windowvariable is defined or not. as in browser it should always be defined.

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

var isBrowser = window!==undefined

回答by Iulian Rotaru

You can create one useful utility with the help of the exenvpackage.

您可以在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 => {};
}

And use it like this

并像这样使用它

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

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

And the function will only be called on client side, and it will log on server side that this function has been called on client side if you set NODE_ENV=development

并且该函数只会在客户端调用,如果您设置,它会在服务器端登录该函数已在客户端调用 NODE_ENV=development

(It's typescript, remove types for JS :) )

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

回答by JoeTidee

At the topmost level of the server Element hierarchy, one could add a ServerContextsuch as this:

在服务器元素层次结构的最顶层,可以添加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>
);

Doing so, it should be possible to read the isServer from the context like this:

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

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