web-dev-qa-db-fra.com

Comment puis-je garder document.title mis à jour dans l'application React?

Étant donné que React n’a aucun moyen intégré de gérer document.title, j’ai l'habitude de le définir dans componentDidMount de mes gestionnaires d'itinéraire.

Cependant, je dois maintenant modifier le titre en fonction de state récupéré de manière asynchrone. J'ai commencé à mettre des évaluations dans componentDidUpdate, mais de temps en temps, j'oublie de mettre une affectation document.title dans certaines pages et le titre précédent reste visible jusqu'à ce que je le remarque enfin.

Idéalement, je voudrais un moyen d’exprimer document.title de manière déclarative, sans avoir à l’affecter. Une sorte de "faux" composant serait probablement plus pratique, étant donné que je veux pouvoir spécifier le titre du document à plusieurs niveaux d'imbrication:

  • Au premier niveau (le titre par défaut);
  • Au niveau de la page (pour certaines pages, mais pas toutes);
  • Parfois, au niveau des composants internes (par exemple, saisie par un utilisateur dans un champ).

Exigences supplémentaires:

  • Le titre spécifié dans l'enfant doit remplacer le titre spécifié par le parent;
  • Fiable (garantit le nettoyage lors du changement d'itinéraire);
  • Ne doit pas émettre de DOM (c.-à-d. Pas de piratage avec le composant retournant <noscript>);
  • J'utilise react-router, mais il est préférable que ce composant fonctionne également avec d'autres routeurs.

Tout ce que je peux utiliser?

27
Dan Abramov

J'ai écrit react-document-title juste pour ça.

Il fournit un moyen déclaratif de spécifier document.title dans une application à page unique.
Si vous souhaitez obtenir le titre sur le serveur après le rendu des composants en chaîne, appelez DocumentTitle.rewind().

Caractéristiques

  • N'émet pas de DOM, pas même un <noscript>;
  • Comme un composant React normal, peut utiliser les variables props et state de son parent;
  • Peut être défini à plusieurs endroits au cours de l'application;
  • Prend en charge des niveaux d’emboîtement arbitraires afin que vous puissiez définir des titres spécifiques à l’application et aux pages;
  • Fonctionne sur le client et le serveur.

Exemple

En supposant que vous utilisiez quelque chose comme react-router :

var App = React.createClass({
  render: function () {
    // Use "My Web App" if no child overrides this
    return (
      <DocumentTitle title='My Web App'>
        <this.props.activeRouteHandler />
      </DocumentTitle>
    );
  }
});

var HomePage = React.createClass({
  render: function () {
    // Use "Home" while this component is mounted
    return (
      <DocumentTitle title='Home'>
        <h1>Home, sweet home.</h1>
      </DocumentTitle>
    );
  }
});

var NewArticlePage = React.createClass({
  mixins: [LinkStateMixin],

  render: function () {
    // Update using value from state while this component is mounted
    return (
      <DocumentTitle title={this.state.title || 'Untitled'}>
        <div>
          <h1>New Article</h1>
          <input valueLink={this.linkState('title')} />
        </div>
      </DocumentTitle>
    );
  }
});

La source

Je garde une trace des instances montées et n'utilise que title donné à la DocumentTitle supérieure dans la pile d'instances montées à chaque mise à jour, montée ou démontée. Sur le serveur, componentWillMount se déclenche mais nous n'obtiendrons pas didMount ou willUnmount; nous introduisons donc DocumentTitle.rewind() qui renvoie une chaîne et détruit l'état pour préparer la prochaine requête.

var DocumentTitle = React.createClass({
  propTypes: {
    title: PropTypes.string
  },

  statics: {
    mountedInstances: [],

    rewind: function () {
      var activeInstance = DocumentTitle.getActiveInstance();
      DocumentTitle.mountedInstances.splice(0);

      if (activeInstance) {
        return activeInstance.props.title;
      }
    },

    getActiveInstance: function () {
      var length = DocumentTitle.mountedInstances.length;
      if (length > 0) {
        return DocumentTitle.mountedInstances[length - 1];
      }
    },

    updateDocumentTitle: function () {
      if (typeof document === 'undefined') {
        return;
      }

      var activeInstance = DocumentTitle.getActiveInstance();
      if (activeInstance) {
        document.title = activeInstance.props.title;
      }
    }
  },

  getDefaultProps: function () {
    return {
      title: ''
    };
  },

  isActive: function () {
    return this === DocumentTitle.getActiveInstance();
  },

  componentWillMount: function () {
    DocumentTitle.mountedInstances.Push(this);
    DocumentTitle.updateDocumentTitle();
  },

  componentDidUpdate: function (prevProps) {
    if (this.isActive() && prevProps.title !== this.props.title) {
      DocumentTitle.updateDocumentTitle();
    }
  },

  componentWillUnmount: function () {
    var index = DocumentTitle.mountedInstances.indexOf(this);
    DocumentTitle.mountedInstances.splice(index, 1);
    DocumentTitle.updateDocumentTitle();
  },

  render: function () {
    if (this.props.children) {
      return Children.only(this.props.children);
    } else {
      return null;
    }
  }
});

module.exports = DocumentTitle;
52
Dan Abramov

Jetez un coup d'œil au casque de la NFL .

20
dbkaplun
class Layout extends React.Component {
  constructor(props){
    super(props);
    document.title = this.props.title;
  }
  render(){
    return(
      <div>
      </div>
    );
  }
}

et puis <Layout title="My Title"/> aussi facile!

6
rajesh_kw

Essayez react-frozenhead , il est en réalité plus sophistiqué que react-document-title - cela nous permet de changer le titre, la description et tout autre élément de la section.

4
Vadim Guzev

En attendant, 3 ans sont passés! ;-)
Si vous souhaitez manipuler d’autres en-têtes de page que le titre (comme la description, les noms canoniques, etc.), la dépendance react-document-meta NPM peut être une bonne chose.

0
Pedro Ferreira