防止在渲染UI Fabric React导航组件时重新加载页面。

4
我在尝试将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 || '#' });
    });
  }

那不起作用。


尝试将其放置在接收事件的箭头函数中。就在你设置状态之前。 - sagi
我试图在componentDidMount中的setState之前添加这样的代码:e.preventDefault(); - 但它不能阻止页面重新加载。 - Flimzy_Programmer
3个回答

3

使用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>
  )
}

1
这个功能非常好,但是如果您使用“Link”或其他路由方法到达导航中的某个位置(而不是导航本身),则导航选择不会更新以匹配路由。在完整页面刷新时它确实会更新,我觉得这很奇怪。对此有什么想法吗? - Emperor Eto

1
我猜您正在使用微软的https://developer.microsoft.com/en-us/fabric#/components/nav#Variants
在这种情况下,您需要在导航项上指定回调。通常,在React中使用像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 }
          ]
        }}
      />
    );
  }
}

你的解决方案有效,谢谢。但在我的情况下,它会导致另一个问题;当我在浏览器中点击“后退/前进”时,除了导航列表选择外,我得到了预期的行为 - 它仍停留在上次点击的列表元素,并没有反映当前的URL。有什么想法如何解决这个问题吗? - Flimzy_Programmer
1
要解决这个问题,您需要订阅当前路由的历史记录,而不是自己的状态。可以使用 https://github.com/ReactTraining/history 和 https://reacttraining.com/react-router/web/api/Router 来完成这个操作。主要思想是,与其自己保留状态,不如将所选的导航键作为属性传递。 - T.Chmelevskij

0
为了防止页面刷新,在Nav组件的onLinkClick事件处理程序中调用event.preventDefault():
<Nav onLinkClick={linkClickHandler} selectedKey={selectedKey} />

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

     console.log(url);
}

除了改变导航控件本身的选择之外,实际上并没有做任何有用的事情。 - Emperor Eto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接