Gatsby.js: Навигация с помощью параметров URL и кнопок браузера «Назад/Вперед»

У меня есть компонент статей, который отображает список сообщений. Список разбит на страницы, поэтому на странице может отображаться не более 10 сообщений. Существует кнопка «Следующая страница», при нажатии которой обновляется состояние компонента и соответствующий параметр URL-адреса, например:

страница 1: /новости

страница 2: /новости?page=2

страница 3: /новости?page=3

...и так далее. То, как настроены методы constructor() и render(), будет считывать этот параметр URL-адреса и отображать правильные сообщения, если пользователь переходит непосредственно к /news?page=3, например.

Проблема, с которой я сталкиваюсь, заключается в том, что кнопки браузера «Назад» и «Вперед», похоже, не перерисовывают страницу. Таким образом, если пользователь несколько раз нажимает кнопку «Следующая страница», а затем нажимает кнопку «Назад», URL-адрес обновится, но страница не будет отображаться повторно. Есть ли способ заставить его сделать это?

Я предполагаю, что есть способ сделать это, добавив прослушиватель window.history, но я не был уверен, есть ли рекомендуемая практика для использования gatsby-link.

Вот урезанная версия компонента для справки:

import React, { Component } from 'react';
import { navigateTo } from 'gatsby-link';
import getUrlParameter from '../functions/getUrlParameter';

export default class extends Component {
  constructor(props) {
    super(props);

    /* external function, will grab value
    * of ?page= url parameter if it exists */
    const urlParamPage = getUrlParameter('page');
    const currentPage = urlParamPage ? urlParamPage : 1;

    this.state = {
      currentPage
    };
  }

  nextPage() {
    const { props, state } = this;

    const urlParam = state.currentPage > 1
      ? `?page=${state.currentPage}`
      : '';

    navigateTo(props.pathname + urlParam);
    this.setState({currentPage: this.state.currentPage + 1});
  }

  render() {
    const { props, state } = this;

    const articles = props.articles
      .slice(state.currentPage * 10, state.currentPage * 10 + 10);

    return (
      <div>
        <ul>
          {articles.map((article) => <li>{article.title}</li>)}
        </ul>

        <button onClick={() => this.nextPage()}>Next Page</button>
      </div>
    );
  }
}

person dougmacklin    schedule 22.02.2018    source источник
comment
Вы можете проверить, срабатывает ли componentWillReceiveProps при нажатии кнопки "назад/вперед"?   -  person Ben    schedule 06.03.2018
comment
хм, он вообще не запускается, при щелчках назад / вперед или начальной загрузке компонента   -  person dougmacklin    schedule 06.03.2018
comment
Какую версию гэтсби вы используете? Также вы видите какие-либо ошибки консоли при навигации с помощью кнопок браузера?   -  person Divyanshu Maithani    schedule 06.03.2018
comment
@DivyanshuMaithani У меня версия gatsby 1.9.158, и ошибок консоли не появляется.   -  person dougmacklin    schedule 06.03.2018


Ответы (2)


Ваша страница не перерисовывается, потому что на самом деле это та же самая страница — компонент уже смонтирован. Поскольку вы извлекаете свои данные в constructor(), ваша страница не будет обновляться, потому что конструктор для компонента React вызывается до его монтирования (источник).

То, что вы называете urlParam, всего лишь новая опора, которую componentWillReceiveProps(nextProps) должен получить в nextProps.location.search.

Редактировать:

Вы должны поднять состояние, потому что только корневой компонент будет получать props.location на кнопках браузера назад и вперед. Свойство pathname вашего компонента Articles изменяется, поэтому componentWillReceiveProps здесь никогда не срабатывает.

Код:

/src/страницы/root.js

import React, { Component } from 'react';
import { navigateTo } from 'gatsby-link';

import Articles from '../components/Articles';

export default class Test extends Component {
  constructor(props) {
    super(props);

    this.state = {
      currentPage: 1,
      data: {}, // your ext data
    };

    this.nextPage = this.nextPage.bind(this);
  }

  nextPage() {
    const { currentPage } = this.state;
    const { pathname } = this.props.location;
    const url = `${pathname}?page=${currentPage + 1}`;

    this.setState({ currentPage: currentPage + 1 });
    navigateTo(url);
  }

  componentWillReceiveProps(nextProps) {
    const { pathname, search } = nextProps.location;
    const getParam = /(\d+)(?!.*\d)/;

    const currentPage = search !== '' ? Number(search.match(getParam)[0]) : 1;

    /* get your ext. data here */
    const data = {};

    this.setState({ currentPage, data });
  }

  render() {
    const { currentPage, data } = this.state;
    return (
      <div>
        {/* other contents here */}
        <Articles
          nextPage={this.nextPage}
          currentPage={currentPage}
          data={data}
        />
      </div>
    );
  }
}

/src/components/Articles.js

import React from 'react';

const Articles = ({ nextPage, currentPage }) => {
  return (
    <div>
      <div>Page: {currentPage}</div>
      <button onClick={() => nextPage()}>Next Page</button>
    </div>
  );
};

export default Articles;
person Nenu    schedule 06.03.2018
comment
Спасибо за ваш ответ @Nenu, однако кажется, что метод жизненного цикла componentWillReceiveProps никогда не вызывается при начальной загрузке страницы или при навигации с помощью кнопки «Следующая страница». - person dougmacklin; 06.03.2018
comment
Это странно. Я нашел здесь, что иногда componentWillReceiveProps не срабатывает по неясной причине... Я рекомендую вам создать новую страницу в вашем проекте gatsby и посмотрите, работает ли componentWillReceiveProps (это то, что я сделал, прежде чем ответить на этот пример кода). Затем попробуйте отладить это или реорганизовать структуру кода. - person Nenu; 06.03.2018
comment
У меня была такая же интуиция, звучит странно, что она не срабатывает при смене маршрута - person Ben; 07.03.2018
comment
Что ж, в моем проекте gatsby страница импортирует и отображает этот компонент. Я предполагаю, что поскольку на самом деле это не ссылка на новую страницу, а вместо этого обновление состояния внутреннего компонента и последующее обновление URL-адреса, это приводит к тому, что кнопки «назад/вперед» не имеют никакого эффекта. Есть ли способ заставить его загружать всю страницу, как если бы она перемещалась с другой? - person dougmacklin; 07.03.2018
comment
Ага, понятно. Ну, я думаю, я понял. Согласно вашему коду, вы передаете этому компоненту свойство pathname, которое должно быть равно props.location.pathname, верно? Если это так, это нормально, что componentWillReceiveProps не срабатывает в вашем компоненте «Статьи», поскольку pathname никогда не меняется. Я бы порекомендовал вам поднять состояния вверх. Я отредактировал свой ответ таким образом. - person Nenu; 07.03.2018
comment
Хм я поднял состояние но componentWillReceiveProps все равно не срабатывает у меня при использовании кнопок назад/вперед. Это работает для вас? - person dougmacklin; 10.03.2018
comment
@dougmacklin Да, у меня хорошо работает код выше. Я думаю, что тогда нам не хватает информации о вашей проблеме... Я думаю, будет проще, если вы сможете предоставить MVCE для проверки вашего код. - person Nenu; 12.03.2018
comment
@Nenu получается, что помимо поднятия состояния мне еще нужно было обновить gatsby/gatsby-link! У меня были версии 1.9.158 и 1.6.34 соответственно и после обновления до 1.9.231 и 1.6.39 заработало. Спасибо за помощь! - person dougmacklin; 13.03.2018

Это можно решить с помощью объекта истории, присутствующего внутри объекта окна.

Другой способ — использовать React-Router-DOM, который является последним модулем маршрутизации для маршрутизации в приложениях ReactJS. Поэтому для ссылки на маршрутизатор V4 следуйте этому коду.

  import {BrowserRouter as Router, Route, Switch, Redirect} from 'react-router-dom';

После импорта маршрутизатора просто попробуйте определить внутри него корневой компонент, после чего вы сможете создавать внутри него маршруты.

  <Router>
    <div className="App" id="App">
      <Route path="/" exact component={Home}/>
      <Route path="/about" exact component={About}/>  
      <Route path="/misreports" exact component={MISReport}/>  
      <Route path="/contact" exact component={Contact}/>  
    </div>
  </Router> 

имейте в виду, что маршрутизатор должен содержать только одного дочернего элемента, иначе он не будет работать должным образом. Таким образом, с маршрутизацией становится очень легко работать.

person Amit Mundra    schedule 06.03.2018
comment
Спасибо за ваш ответ @Amit, однако я не думаю, что решение для реактивного маршрутизатора применимо к этому конкретному случаю. Не могли бы вы продемонстрировать решение, используя объект истории? - person dougmacklin; 06.03.2018