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

时间:2023-01-05 07:16:06

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()这样的东西。

4 个解决方案

#1


21  

Internally, React uses a utility called ExecutionEnvironment for this. It implements a few useful properties like canUseDOM and canUseEventListeners. The solution is essentially just what's suggested here though.

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

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

EDIT This is an undocumented feature that shouldn't be used directly. It's 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 EDIT Someone created an npm package for this code. I suggest using that.

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

npm install exenv --save

#2


5  

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 componentDidMount only runs on the client, but not on the server.

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

#3


0  

At the topmost level of the server Element hierarchy, one could add a ServerContext such 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
);

#4


0  

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

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

#1


21  

Internally, React uses a utility called ExecutionEnvironment for this. It implements a few useful properties like canUseDOM and canUseEventListeners. The solution is essentially just what's suggested here though.

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

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

EDIT This is an undocumented feature that shouldn't be used directly. It's 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 EDIT Someone created an npm package for this code. I suggest using that.

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

npm install exenv --save

#2


5  

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 componentDidMount only runs on the client, but not on the server.

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

#3


0  

At the topmost level of the server Element hierarchy, one could add a ServerContext such 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
);

#4


0  

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

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