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

时间:2015-08-28 00:16:20

标签: reactjs react-router

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

myapplication.com/project/unique-project-id

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

myapplication.com/project/982378632
myapplication.com/project/782387223
myapplication.com/project/198731289

不会再次触发componentDidMount,因此不会刷新数据。我是否应该使用其他生命周期事件来触发我的数据请求或采用不同的策略来解决此问题?

12 个答案:

答案 0 :(得分:72)

如果在路线更改时确实需要重新装配组件,则可以将唯一键传递给组件的键属性(该键与您的路径/路径相关联)。因此,每次路由更改时,密钥也会更改,这会触发React组件卸载/重新装入。我从this answer

得到了这个想法

答案 1 :(得分:63)

如果链接仅使用不同的参数指向同一路线,则它不会重新安装,而是接收新的道具。因此,您可以使用componentWillReceiveProps(newProps)函数并查找newProps.params.projectId

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

答案 2 :(得分:39)

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

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

答案 3 :(得分:10)

您必须明确,路线更改不会导致页面刷新,您必须自己处理。

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 .../> }
}

答案 4 :(得分:4)

这是我解决问题的方法:

此方法从API获取单个项:

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

我从componentDidMount调用此方法,当我第一次加载此路由时,该方法将仅调用一次:

componentDidMount() {   
    const id = this.props.match.params.id;
    this.loadConstruction( id )
  }

然后从componentWillReceiveProps中调用,因为第二次加载相同的路由但使用了不同的ID,因此我调用第一个方法重新加载状态,然后component将加载新项。

componentWillReceiveProps(nextProps) {
    if (nextProps.match.params.id !== this.props.match.params.id) {
      const id = nextProps.match.params.id
      this.loadConstruction( id );
    }
  }

答案 5 :(得分:4)

如果您有:

<Route
   render={(props) => <Component {...props} />}
   path="/project/:projectId/"
/>

在React 16.8及更高版本的using hooks中,您可以执行以下操作:

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

  return (<div>Layout</div>);
}
export default Component;

在那里,只要fetchResource发生变化,您就只会触发一个新的props.match.params.id呼叫。

答案 6 :(得分:3)

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

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

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

<Router>
  <Switch>
    <RemountingRoute path="/item/:id" exact={true} component={ItemPage} />
    <RemountingRoute path="/stuff/:id" exact={true} component={StuffPage} />
  </Switch>
</Router>

<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
}

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

答案 7 :(得分:2)

@wei's answer的效果很好,但在某些情况下,我发现最好不要设置内部组件的键,而是自行路由。另外,如果组件的路径是静态的,但是您只希望每次用户导航到组件时都重新安装组件(也许在componentDidMount()进行api调用),则可以方便地将location.pathname设置为route的键。有了路由,当位置更改时,所有内容都会重新安装。

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)

答案 8 :(得分:0)

如果您正在使用类组件,则可以使用 componentDidUpdate

componentDidMount() {
    const { projectId } = this.props.match.params
    this.GetProject(id); // Get the project when Component gets Mounted
 }

componentDidUpdate(prevProps, prevState) {
    const { projectId } = this.props.match.params

    if (prevState.projetct) { //Ensuring This is not the first call to the server
      if(projectId !== prevProps.match.params.projectId ) {
        this.GetProject(projectId); // Get the new Project when project id Change on Url
      }
    }
 }

答案 9 :(得分:0)

您可以使用提供的方法:

useEffect(() => {
  // fetch something
}, [props.match.params.id])

当保证在更改路线后重新渲染组件时,您可以通过 道具作为依赖

这不是最好的方法,但是足以应付您的需求 根据 Kent C Dodds : 考虑更多您想发生的事情。

答案 10 :(得分:0)

这是一个非常简单的解决方案:在 componentDidUpdate 中进行位置检查,并有一个 getData 函数,该函数具有带 setState 的数据获取部分:

componentDidUpdate (prevProps) {
    if (prevProps.location.key !== this.props.location.key) {
        this.getData();
    }
}


getData = () => {
    CallSomeAsyncronousServiceToFetchData
        .then(
            response => {
                this.setState({whatever: response.data})
            }
        )
}

答案 11 :(得分:-2)

react-router已被破坏,因为它必须在任何位置更改时重新安装组件。

我设法找到了这个bug的修复程序:

https://github.com/ReactTraining/react-router/issues/1982#issuecomment-275346314

简而言之(请参阅上面的链接获取完整信息)

<Router createElement={ (component, props) =>
{
  const { location } = props
  const key = `${location.pathname}${location.search}`
  props = { ...props, key }
  return React.createElement(component, props)
} }/>

这将使其在任何URL更改时重新安装