Предотвратить перезагрузку страницы при рендеринге навигационного компонента реакции ткани пользовательского интерфейса

Я застрял, пытаясь заставить компонент ui-fabric Nav работать с react-router-dom v4+. Мое решение «работает», но перерисовывается вся страница, а не только компонент NavSelection. После некоторых исследований я понял, что мне нужно где-то сделать e.preventDefault(), но я не могу понять, где его добавить.

Главная страница:

    export const Home = () => {
        return (
            <div className="ms-Grid-row">
                <div className="ms-Grid-col ms-u-sm6 ms-u-md4 ms-u-lg2">
                    <Navbar />
                </div>
                <div className="ms-Grid-col ms-u-sm6 ms-u-md8 ms-u-lg10">
                    <NavSelection />
                </div>
            </div>
        );
    }

Панель навигации:

const navGroups = [
  {
    links: [
      { name: 'Name1', url: '/Name1', key: '#Name1' },
      { name: 'Name2', url: '/Name2', key: '#Name2' }
    ]
  }
];

export class Navbar extends React.Component<any, any> {
  constructor(props: INavProps) {
    super(props);

    this.state = {
      selectedNavKey: '#Name1'
    };
  }

  public componentDidMount() {
    window.addEventListener('hashchange', (e) => {
      this.setState({ selectedNavKey: document.location.hash || '#' });
    });
  }

  public render(): JSX.Element {
    const { selectedNavKey } = this.state;

    return (
      <Nav
        selectedKey={selectedNavKey}
        groups={navGroups}
      />
    );
  }
}

Выбор навигации:

export const NavSelection = () => {
    return (
        <div>
            <Route path="/Name1" component={Component1} />
            <Route path="/Name2" component={Component2} />
        </div>
    );
}

Любая помощь приветствуется

Изменить: я пытался поместить его внутрь componentDidMount следующим образом:

public componentDidMount() {
    window.addEventListener('hashchange', (e) => {
      e.preventDefault();
      this.setState({ selectedNavKey: document.location.hash || '#' });
    });
  }

Это не работает.


person Flimzy_Programmer    schedule 29.08.2018    source источник
comment
Попробуйте разместить его внутри стрелочных функций, которые получают событие. Непосредственно перед тем, как установить состояние   -  person sagi    schedule 29.08.2018
comment
Я попытался добавить его в componentDidMount непосредственно перед setState следующим образом: e.preventDefault(); - но это не предотвращает перезагрузку страницы   -  person Flimzy_Programmer    schedule 29.08.2018


Ответы (3)


Я предполагаю, что вы используете Microsoft https://developer.microsoft.com/en-us/fabric#/components/nav#Variants

В этом случае вам нужно указать обратный вызов в элементе навигации. Обычно использование таких вещей, как window.addEventListener в реакции, является антипаттерном.

Это будет выглядеть примерно так.

export class Navbar extends React.Component<any, any> {
  constructor(props: INavProps) {
    super(props);

    this.state = {
      selectedNavKey: '#Name1'
    };
  }

  public handleNavClick(event, { key, url }) {
      // You also need to manually update the router something like
      history.push(url);
      this.setState({ selectedNavKey: key });
  }

  public render(): JSX.Element {
    const { selectedNavKey } = this.state;

    return (
      <Nav
        selectedKey={selectedNavKey}
        groups={{
          links: [
            { name: 'Name1', url: '/Name1', key: '#Name1', onClick: this.handleNavClick },
            { name: 'Name2', url: '/Name2', key: '#Name2', onClick: this.handleNavClick }
          ]
        }}
      />
    );
  }
}
person T.Chmelevskij    schedule 29.08.2018
comment
Ваше решение работает, спасибо. В моем случае это приводит к другой проблеме; когда я нажимаю назад/вперед в браузере, я получаю ожидаемое поведение для всего, кроме выбора списка навигации - он остается последним щелкнутым элементом списка и не отражает текущий URL-адрес. Есть идеи, как это решить? - person Flimzy_Programmer; 29.08.2018
comment
Чтобы исправить это, вам нужно подписаться на историю для текущего маршрута, а не для вашего собственного состояния. Это можно сделать с помощью github.com/ReactTraining/history и reacttraining.com/react-router/web/api/Router. Основная идея заключается в том, что вместо того, чтобы сохранять состояние самостоятельно, вы должны передать selectedNavKey в качестве реквизита. - person T.Chmelevskij; 30.08.2018

Используйте HashRouter вместо BrowserRouter.

Пример:

Маршрутизатор:

...

import { Switch, Route, Redirect, HashRouter } from 'react-router-dom'

...

export const Router: React.FunctionComponent = () => {

  // persisted to localStorage
  const navActiveItem = useSelector(selectNavActiveItem)

  return (
    <Suspense fallback={<LargeSpinner />}>
      <HashRouter>
        <Switch>
          <Route exact path="/" render={() => (
            <Redirect to={navActiveItem.url} />
          )}/>

          <Route exact path="/dashboard/overview" component={Overview} />
          <Route exact path="/dashboard/progress" component={Progress} />
          <Route exact path="/dashboard/issues" component={Issues} />

          ...

        </Switch>
      </HashRouter>
    </Suspense>
  )
}

Навигация:

...

const navLinkGroups: INavLinkGroup[] = [
  {
    name: 'Dashboard',
    expandAriaLabel: 'Expand Dashboard section',
    collapseAriaLabel: 'Collapse Dashboard section',
    links: [
      {
        key: 'DashboardOverview',
        name: 'Overview',
        icon: 'BIDashboard',
        url: '#/dashboard/overview',
      },
      {
        key: 'DashboardProgress',
        name: 'Progress',
        icon: 'TimelineProgress',
        url: '#/dashboard/progress',
      },
      {
        key: 'DashboardIssues',
        name: 'Issues',
        icon: 'ShieldAlert',
        url: '#/dashboard/issues',
      },
    ],
  },
...

export const Navigation: React.FunctionComponent = () => {
  const navActiveItem = useSelector(selectNavActiveItem)
  const dispatch = useDispatch()

  const onLinkClick = (ev?: React.MouseEvent<HTMLElement>, item?: INavLink) => {
    dispatch(setNavActiveItem(item || { name: '', url: '/' }))
  }

  return (
    <Stack tokens={stackTokens} styles={stackStyles}>
      <Nav
        styles={navStyles}
        ariaLabel="Navigation"
        groups={navLinkGroups}
        onLinkClick={onLinkClick}
        initialSelectedKey={navActiveItem.key}
      />
    </Stack>
  )
}
person Phierru    schedule 25.11.2020
comment
Это работает очень хорошо, за исключением того, что если вы используете Link или другой метод маршрутизации к одному из местоположений в навигации, кроме самой навигации, выбор навигации не обновляется в соответствии с маршрутом. Это ДЕЙСТВУЕТ при полном обновлении страницы, что я нахожу странным. Есть мысли по этому поводу? - person Peter Moore; 16.04.2021

Чтобы предотвратить обновление страницы, вызовите event.preventDefault() внутри обработчика событий onLinkClick компонента Nav:

<Nav onLinkClick={linkClickHandler} selectedKey={selectedKey} />

function linkClickHandler(event,{key, url}){
     event.preventDefault();
     setSelectedKey(key);

     console.log(url);
}
person Wei    schedule 13.12.2019
comment
На самом деле не делает ничего полезного, кроме изменения выбора на самом элементе управления навигацией. - person Peter Moore; 13.04.2021