Подтвердить что ты не робот

Как сохранить document.title в приложении React?

Так как у React нет встроенного способа управления document.title, я использовал его внутри componentDidMount для моих обработчиков маршрутов.

Однако теперь мне нужно изменить заголовок на основе state асинхронно. Я начал размещать assingsments в componentDidUpdate, но время от времени я забываю назначить document.title на некоторые страницы, а предыдущий заголовок опускается до тех пор, пока я его не замету.

В идеале я хотел бы выразить декларативно document.title, не назначая его. Какой-то "поддельный" компонент, вероятно, был бы наиболее удобным, учитывая, что я хочу иметь возможность указывать заголовок документа на нескольких уровнях вложенности:

  • На верхнем уровне (название по умолчанию);
  • На уровне страницы (для некоторых страниц, но не для всех);
  • Иногда на внутреннем уровне компонента (например, пользователь вводит в поле).

Дополнительные требования:

  • Заголовок, указанный в дочернем, должен переопределять заголовок, указанный родителем;
  • Надежный (гарантирует очистку при изменении маршрута);
  • Не следует удалять DOM (то есть без хаков с возвратом компонента <noscript>);
  • Я использую response-router, но лучше, если этот компонент работает и с другими маршрутизаторами.

Что я могу использовать?

4b9b3361

Ответ 1

Я написал response-document-title только для этого.

Он предоставляет декларативный способ указать document.title в одностраничном приложении.
Если вы хотите получить титул на сервере после рендеринга компонентов в строку, вызовите DocumentTitle.rewind().

Функции

  • Не испускает DOM, даже не <noscript>;
  • Как обычный компонент React, может использовать его родительский props и state;
  • Может быть определено во многих местах приложения;
  • Поддерживает произвольные уровни вложенности, поэтому вы можете определять заголовки приложений и страниц,
  • Работает на клиенте и сервере.

Пример

Предполагая, что вы используете что-то вроде 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>
    );
  }
});

Источник

Я отслеживаю смонтированные экземпляры и использую только title для верхнего DocumentTitle в смонтированном стеке экземпляра всякий раз, когда он обновляется, монтируется или размонтируется. На сервере componentWillMount срабатывает, но мы не получаем didMount или willUnmount, поэтому вводим DocumentTitle.rewind(), который возвращает строку и уничтожает состояние для подготовки следующего запроса.

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;

Ответ 3

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

а затем <Layout title="My Title"/> легко!

Ответ 4

Попробуйте react-frozenhead, это на самом деле сложнее, чем response-document-title - это позволяет нам изменять заголовок, описание и все остальное в разделе.

Ответ 5

Между тем, прошло 3 года!;-)
Если вы хотите манипулировать другими заголовками страниц, чем заголовком (например, описанием, каноническим и т.д.), react-document-meta NPM-зависимость может быть хорошей вещью для использования.