경로 매개 변수가 변경 될 때 구성 요소가 다시 마운트되지 않음


97

react-router를 사용하여 반응 응용 프로그램을 작업 중입니다. 다음과 같은 URL이있는 프로젝트 페이지가 있습니다.

myapplication.com/project/unique-project-id

프로젝트 구성 요소가로드되면 componentDidMount 이벤트에서 해당 프로젝트에 대한 데이터 요청을 트리거합니다. 이제 두 프로젝트간에 직접 전환하면 ID 만 이와 같이 변경되는 문제가 발생합니다.

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

componentDidMount가 다시 트리거되지 않으므로 데이터가 새로 고쳐지지 않습니다. 데이터 요청을 트리거하는 데 사용해야하는 다른 수명주기 이벤트 나이 문제를 해결하기위한 다른 전략이 있습니까?


1
구성 요소 코드를 게시 할 수 있습니까?
Pierre Criulanscy

답변:


81

링크가 다른 매개 변수 만 사용하여 동일한 경로로 향하는 경우 다시 마운트되지 않고 대신 새 소품을받습니다. 따라서 componentWillReceiveProps(newProps)함수를 사용하고 newProps.params.projectId.

데이터를로드하려는 경우 라우터가 구성 요소의 정적 메서드를 사용하여 일치를 처리하기 전에 데이터를 가져 오는 것이 좋습니다. 이 예를 확인하십시오. React Router Mega Demo . 이렇게하면 구성 요소가 데이터를로드하고 경로 매개 변수가 변경 될 때 자동으로 업데이트됩니다 componentWillReceiveProps.


2
감사합니다. componentWillReceiveProps를 사용하여이 작업을 수행 할 수있었습니다. 여전히 React Router Mega Demo 데이터 흐름을 이해하지 못합니다. 일부 구성 요소에 정의 된 정적 fetchData가 있습니다. 이러한 정적은 라우터 클라이언트 측에서 어떻게 호출됩니까?
Constellates

1
좋은 질문입니다. client.js 파일과 server.js 파일을 확인하십시오. 라우터의 실행주기 동안 fetchData.js 파일을 호출하여 전달합니다 state. 처음에는 조금 혼란 스럽지만 조금 더 명확 해집니다.
Brad B

12
참고 : "componentWillReceiveProps"는 레거시로 간주
Idan Dagan

4
componentWillReceiveProps가 더 이상 사용되지 않으므로 React 16과 함께 ComponentDidUpdate를 사용해야합니다.
Pato Loco

1
이것은 작동하지만 데이터를로드하는 모든 구성 요소에 다시 렌더링을 처리하기 위해 componentWillReceiveProps () 또는 componentDidUpdate ()를 추가해야한다는 문제가 있습니다. 예를 들어 동일한 경로 매개 변수를 기반으로 데이터를로드하는 여러 구성 요소가있는 페이지를 생각해보십시오.
Juha Syrjälä

97

경로가 변경 될 때 구성 요소를 다시 마운트해야하는 경우 구성 요소의 키 속성에 고유 한 키를 전달할 수 있습니다 (키는 경로 / 경로와 연결됨). 따라서 경로가 변경 될 때마다 React 구성 요소가 마운트 해제 / 재 마운트하도록 트리거하는 키도 변경됩니다. 이 답변 에서 아이디어를 얻었습니다.


2
훌륭한! 이 간단하고 우아한 답변을 주셔서 감사합니다
Z_z_Z

이것이 앱의 성능에 영향을 미치지 않습니까?
Alpit Anand

1
@AlpitAnand 이것은 광범위한 질문입니다. 다시 마운트하는 것은 더 많은 수명주기 메서드를 트리거하므로 다시 렌더링하는 것보다 확실히 느립니다. 여기에서는 경로가 변경 될 때 다시 마운트하는 방법에 대한 답변입니다.
wei

하지만 큰 영향은 없나요? 별다른 효과없이 안전하게 사용할 수있는 조언은 무엇입니까?
Alpit Anand

@AlpitAnand 귀하의 질문은 너무 광범위하고 응용 프로그램에 따라 다릅니다. 일부 응용 프로그램의 경우 성능 저하가 발생할 수 있습니다.이 경우 소품이 자신을 변경하는 것을보고 업데이트해야하는 요소 만 업데이트해야합니다. 하지만 대부분의 경우 위의 방법이 좋은 해결책입니다
Chris

83

위의 일부와 비슷하지만 코드가있는 내 대답은 다음과 같습니다.

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

4
여기의 핵심은 프로필 페이지에 링크가있는 이미지를 클릭하면 동일한 경로로 리디렉션되지만 다른 매개 변수를 사용하여 구성 요소가 업데이트되지 않기 때문에 가장 큰 역할을합니다. React가 차이점을 찾을 수 없기 때문입니다. 이전 결과를 비교하려면 KEY가 있어야하기 때문에
Georgi Peev

14

경로 변경으로 인해 페이지가 새로 고쳐지지는 않는다는 점을 분명히해야하며 직접 처리해야합니다.

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

감사합니다.이 솔루션은 저에게 효과적입니다. 하지만 내 eslint 설정이 이것에 대해 불평하고 있습니다 : github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/… 이것에 대해 어떻게 생각하십니까?
konekoya

예, 실제로는 모범 사례가 아닙니다. 죄송합니다. "fetchProject"를 발송 한 후 리듀서가 어쨌든 소품을 업데이트 할 것입니다. 저는 redux를 제자리에 두지 않고 내 요점을 만들기 위해 이것을 사용했습니다
chickenchilli

안녕하세요 chickenchilli, 나는 실제로 여전히 이것에 갇혀 있습니다 ... 다른 제안이나 권장 튜토리얼이 있습니까? 아니면 지금은 귀하의 솔루션을 고수하겠습니다. 당신의 도움을 주셔서 감사합니다!
konekoya

12

당신이 가지고 있다면:

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

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;

거기에서는 변경 fetchResource될 때마다 새 호출을 트리거합니다 props.match.params.id.


4
대부분의 다른 답변이 현재 사용되지 않고 안전하지 않은 답변에 의존한다는 점을 감안할 때 더 나은 답변입니다componentWillReceiveProps
pjb

7

@wei, @ Breakpoint25 및 @PaulusLimma의 답변을 바탕으로 <Route>. 이렇게하면 URL이 변경 될 때 페이지가 다시 마운트되어 페이지의 모든 구성 요소가 다시 렌더링되는 것이 아니라 생성되고 다시 마운트됩니다. 모든 componentDidMount()다른 시작 후크는 URL 변경시에도 실행됩니다.

아이디어는 keyURL이 변경 될 때 컴포넌트 속성을 변경하는 것이며 이로 인해 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으로 테스트되었습니다.


5

@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)

5

이것이 내가 문제를 해결 한 방법입니다.

이 메서드는 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를로드하고 첫 번째 메서드를 호출하여 상태를 다시로드하면 구성 요소가 새 항목을로드합니다.

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

이것은 작동하지만 데이터를로드하는 모든 구성 요소에 다시 렌더링을 처리하기 위해 componentWillReceiveProps ()를 추가해야한다는 문제가 있습니다.
Juha Syrjälä

componentDidUpdate (prevProps)를 사용하십시오. componentWillUpdate () componentWillReceiveProps ()는 더 이상 사용되지 않습니다.
Mirek Michalak 2019

0

Class Component를 사용하는 경우 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
      }
    }
 }

componentDidUpdate는 이제 더 이상 사용되지 않습니다. 대안을 제안 할 수 있다면 좋을 것입니다.
Sahil

확실합니까? ComponentDidUpdate가 아니며 향후 버전에서 더 이상 사용되지 않을 것입니다. 링크를 첨부 할 수 있습니까? 다음과 같이 폐기 될 함수 : componentWillMount — UNSAFE_componentWillMount componentWillReceiveProps — UNSAFE_componentWillReceiveProps componentWillUpdate — UNSAFE_componentWillUpdate
Angel Mas

0

제공된 방법을 사용할 수 있습니다.

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

경로 변경 후 구성 요소가 다시 렌더링되도록 보장되면 소품을 종속성으로 전달할 수 있습니다.

Kent C Dodds 에 따르면 최선의 방법은 아니지만 원하는 것을 처리하기에 충분합니다 .


-3

react-router 위치 변경시 구성 요소를 다시 장착해야하기 때문에 손상되었습니다.

그래도이 버그에 대한 수정 사항을 찾았습니다.

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 변경시 다시 마운트됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.