web-dev-qa-db-fra.com

Dans React, comment détecter si le composant est rendu par le client ou le serveur?

Je construis une application isomorphe, mais j'utilise un composant tiers qui effectue uniquement le rendu sur le client. Donc, en particulier pour ce composant, je n'ai besoin de le rendre que lorsque je suis dans le rendu du client.

Comment détecter si je suis sur le client ou sur le serveur? Je cherche quelque chose comme isClient() ou isServer().

20
André Pena

En interne, React utilise pour cela un utilitaire appelé ExecutionEnvironment. Il implémente quelques propriétés utiles comme canUseDOM et canUseEventListeners. La solution est essentiellement juste ce qui est suggéré ici bien.

L'implémentation de canUseDOM

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

Je l'utilise dans mon application comme ça

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

EDIT Il s'agit d'une fonctionnalité non documentée qui ne devrait pas être utilisée directement. Son emplacement changera probablement de version en version. J'ai partagé cela comme un moyen de dire "c'est le mieux que vous puissiez faire" en montrant ce que l'équipe de Facebook utilise en interne. Vous voudrez peut-être copier ce code (très petit) dans votre propre projet afin de ne pas avoir à vous soucier de suivre son emplacement, d'une version à l'autre, ni de changements importants.

ANOTHER EDIT Quelqu'un a créé un paquet npm pour ce code. Je suggère d'utiliser cela.

npm install exenv --save
27
Charlie Martin

Deux choses qui peuvent être pertinentes:

De nombreux projets utilisent une convention selon laquelle ils définissent un booléen global SERVER ou CLIENT afin que tout votre code puisse basculer en fonction de celui-ci. Dans votre groupe de serveurs, définissez une valeur globale, comme dans ce projet

global.__SERVER__ = true;

Et dans votre bundle client, définissez un client global sur true, que vous pouvez réaliser d’une seule manière avec DefinePlugin de Webpack

new webpack.DefinePlugin({
  __CLIENT__: true
})

Avec l'approche ci-dessus, vous pouvez désactiver cette variable dans willMount ou effectuer un rendu pour effectuer une opération sur le serveur et une autre sur le client.

La deuxième chose qui peut être utile ici est que componentDidMount ne fonctionne que sur le client, mais pas sur le serveur. 

6
Andy Ray

Vous pouvez également utiliser componentDidMount(), car cette méthode de cycle de vie n'est pas exécutée lorsque la page est rendue côté serveur.

2
JoeTidee

Vous pouvez créer un utilitaire utile à l’aide du package 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 => {};
}

Et l'utiliser comme ça

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

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

Et la fonction ne sera appelée que du côté client et le serveur saura que cette fonction a été appelée côté client si vous définissez NODE_ENV=development

(C'est TypeScript, supprimez les types pour JS :))

0
Iulian Rotaru

Au niveau le plus élevé de la hiérarchie des éléments du serveur, vous pouvez ajouter une variable ServerContext telle que:

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>
);

Ce faisant, il devrait être possible de lire isServer à partir du contexte comme ceci:

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