路径参数更改时,组件不会重新装入

Con*_*tes 79 reactjs react-router

我正在使用react-router处理反应应用程序.我有一个项目页面,其中包含如下网址:

myapplication.com/project/unique-project-id
Run Code Online (Sandbox Code Playgroud)

当项目组件加载时,我从componentDidMount事件触发该项目的数据请求.我现在遇到一个问题,如果我直接在两个项目之间切换,所以只有id改变这样......

myapplication.com/project/982378632
myapplication.com/project/782387223
myapplication.com/project/198731289
Run Code Online (Sandbox Code Playgroud)

componentDidMount不会再次触发,因此不会刷新数据.我应该使用另一个生命周期事件来触发我的数据请求,还是采用不同的策略来解决这个问题?

wei*_*wei 84

如果在路由更改时确实需要组件重新装入,则可以将唯一键传递给组件的键属性(该键与路径/路径关联).因此,每次路由更改时,密钥也会更改,这会触发React组件卸载/重新装入.我从这个答案中得到了这个想法

  • 谢谢.这应该是接受的答案 (3认同)
  • 这绝对棒,谢谢! (2认同)
  • 辉煌!感谢您提供这个简单而优雅的答案 (2认同)

Bra*_*ugh 70

如果链接指向同一路线只有一个不同的参数,它不会重新安装,而是接收新的道具.所以,你可以使用该componentWillReceiveProps(newProps)功能并寻找newProps.params.projectId.

如果您正在尝试加载数据,我建议在路由器使用组件上的静态方法处理匹配之前获取数据.看看这个例子.反应路由器Mega演示.这样,组件将加载数据并在路径参数更改时自动更新,而无需依赖componentWillReceiveProps.

  • 仅供参考:"componentWillReceiveProps"被认为是遗产 (9认同)
  • 你应该在 React 16 中使用 ComponentDidUpdate 因为 componentWillReceiveProps 已被弃用 (5认同)
  • 谢谢.我能够使用componentWillReceiveProps来实现这一点.我仍然不太了解React Router Mega Demo数据流.我看到在一些组件上定义的静态fetchData.如何从路由器客户端调用这些静态? (2认同)

Bre*_*t25 57

这是我的答案,类似于上面的一些但有代码.

<Route path="/page/:pageid" render={(props) => (
  <Page key={props.match.params.pageid} {...props} />)
} />
Run Code Online (Sandbox Code Playgroud)

  • 这里的关键起着最大的作用,因为你在个人资料页面中链接到的图像,单击它后只是重定向到相同的路由,但具有不同的参数,但组件不会更新,因为React无法找到差异,因为必须有一个KEY来比较旧的结果 (7认同)

chi*_*lli 12

您必须清楚,路由更改不会导致页面刷新,您必须自己处理它.

import theThingsYouNeed from './whereYouFindThem'

export default class Project extends React.Component {

    componentWillMount() {

        this.state = {
            id: this.props.router.params.id
        }

        // fire action to update redux project store
        this.props.dispatch(fetchProject(this.props.router.params.id))
    }

    componentDidUpdate(prevProps, prevState) {
         /**
         * this is the initial render
         * without a previous prop change
         */
        if(prevProps == undefined) {
            return false
        }

        /**
         * new Project in town ?
         */
        if (this.state.id != this.props.router.params.id) {
           this.props.dispatch(fetchProject(this.props.router.params.id))
           this.setState({id: this.props.router.params.id})
        }

    }

    render() { <Project .../> }
}
Run Code Online (Sandbox Code Playgroud)


Alf*_*rez 8

如果你有:

<Route
   render={(props) => <Component {...props} />}
   path="/project/:projectId/"
/>
Run Code Online (Sandbox Code Playgroud)

在React 16.8及更高版本中,使用hooks可以执行以下操作:

import React, { useEffect } from "react";
const Component = (props) => {
  useEffect(() => {
    props.fetchResource();
  }, [props.match.params.projectId]);

  return (<div>Layout</div>);
}
export default Component;
Run Code Online (Sandbox Code Playgroud)

在这里,fetchResource只要有props.match.params.id更改,您就只会触发一个新呼叫。

  • 鉴于大多数其他答案都依赖于现已弃用且不安全的componentWillReceiveProps,因此这是更好的答案 (3认同)

Pau*_*mma 7

@wei 的回答很有效,但在某些情况下,我发现最好不要设置内部组件的键,而是设置路由本身。此外,如果组件的路径是静态的,但您只想在每次用户导航到它时重新安装组件(可能在 componentDidMount() 进行 api 调用),则将 location.pathname 设置为路由的键很方便。有了它,当位置改变时,它的路线和它的所有内容都会被重新安装。

const MainContent = ({location}) => (
    <Switch>
        <Route exact path='/projects' component={Tasks} key={location.pathname}/>
        <Route exact path='/tasks' component={Projects} key={location.pathname}/>
    </Switch>
);

export default withRouter(MainContent)
Run Code Online (Sandbox Code Playgroud)


小智 5

我是这样解决问题的:

此方法从 API 获取单个项目:

loadConstruction( id ) {
    axios.get('/construction/' + id)
      .then( construction => {
        this.setState({ construction: construction.data })
      })
      .catch( error => {
        console.log('error: ', error);
      })
  }
Run Code Online (Sandbox Code Playgroud)

我从 componentDidMount 调用这个方法,这个方法只会被调用一次,当我第一次加载这个路由时:

componentDidMount() {   
    const id = this.props.match.params.id;
    this.loadConstruction( id )
  }
Run Code Online (Sandbox Code Playgroud)

从第二次加载相同的路由但不同的 ID 开始调用 componentWillReceiveProps,我调用第一个方法重新加载状态,然后组件将加载新项目。

componentWillReceiveProps(nextProps) {
    if (nextProps.match.params.id !== this.props.match.params.id) {
      const id = nextProps.match.params.id
      this.loadConstruction( id );
    }
  }
Run Code Online (Sandbox Code Playgroud)


Juh*_*älä 5

根据@ wei,@ Breakpoint25和@PaulusLimma的回答,我为制作了此替换组件<Route>。URL更改时,这将重新安装页面,迫使页面中的所有组件都被创建并重新安装,而不仅仅是重新呈现。所有componentDidMount()其他所有启动挂钩也都在URL更改时执行。

这个想法是key在URL更改时更改组件属性,这将迫使React重新安装组件。

您可以将其用作的替代产品<Route>,例如:

<Router>
  <Switch>
    <RemountingRoute path="/item/:id" exact={true} component={ItemPage} />
    <RemountingRoute path="/stuff/:id" exact={true} component={StuffPage} />
  </Switch>
</Router>
Run Code Online (Sandbox Code Playgroud)

<RemountingRoute>组件的定义如下:

export const RemountingRoute = (props) => {
  const {component, ...other} = props
  const Component = component
  return (
    <Route {...other} render={p => <Component key={p.location.pathname + p.location.search}
                                              history={p.history}
                                              location={p.location}
                                              match={p.match} />}
    />)
}

RemountingRoute.propsType = {
  component: PropTypes.object.isRequired
}
Run Code Online (Sandbox Code Playgroud)

这已经通过React-Router 4.3进行了测试。