ルートパラメータの変更時にコンポーネントが再マウントされない


97

私はreact-routerを使用して反応アプリケーションに取り組んでいます。次のようなURLのプロジェクトページがあります。

myapplication.com/project/unique-project-id

プロジェクトコンポーネントが読み込まれると、componentDidMountイベントからそのプロジェクトのデータリクエストがトリガーされます。2つのプロジェクトを直接切り替えると、次のようにIDのみが変更されるという問題が発生しています...

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

componentDidMountは再度トリガーされないため、データは更新されません。データ要求をトリガーするために使用する必要がある別のライフサイクルイベントや、この問題に取り組むための別の戦略はありますか?


1
コンポーネントのコードを投稿できますか?
Pierre Criulanscy

回答:


81

リンクが同じパラメーターに異なるパラメーターのみを使用している場合、リンクは再マウントせず、代わりに新しい小道具を受け取ります。したがって、componentWillReceiveProps(newProps)関数を使用してを探すことができますnewProps.params.projectId

データをロードしようとしている場合は、ルーターがコンポーネントの静的メソッドを使用して一致を処理する前にデータをフェッチすることをお勧めします。この例を確認してください。Reactルーターメガデモ。このように、コンポーネントはデータをロードし、ルートパラメータが変更されたときに自動的に更新されるため、に依存する必要はありませんcomponentWillReceiveProps


2
ありがとうございました。私はcomponentWillReceivePropsを使用してこれを機能させることができました。私はまだReact Router Mega Demoのデータフローを本当に理解していません。一部のコンポーネントで定義されている静的なfetchDataが表示されます。これらの統計はルータークライアント側からどのように呼び出されますか?
2015

1
すばらしい質問です。client.jsファイルとserver.jsファイルを確認してください。ルーターの実行サイクル中に、fetchData.jsファイルを呼び出して、それに渡しますstate。最初は少し混乱しますが、少しわかりやすくなります。
ブラッドB

12
FYI:「componentWillReceiveProps」はレガシーと見なされました
Idan Dagan

4
componentWillReceivePropsは非推奨であるため、React 16ではComponentDidUpdateを使用する必要があります
Pato Loco '29

1
これは機能しますが、データをロードしているすべてのコンポーネントへの再レンダリングを処理するために、componentWillReceiveProps()またはcomponentDidUpdate()を追加する必要があるという問題があります。たとえば、同じパスパラメータに基づいてデータを読み込んでいるいくつかのコンポーネントがあるページについて考えてみます。
JuhaSyrjälä19年

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が違いを見つけることができないためです。古い結果を比較するためのキーが必要だから
Georgi Peev '11 / 11/19

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

こんにちは、チキンチリ、私はまだこれで立ち往生しています...他の提案や推奨チュートリアルはありますか?または、私は今のところあなたの解決策に固執します。ご協力いただきありがとうございます!
こねこや2017年

12

あなたが持っている場合:

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

React 16.8以降では、フックを使用して次のことができます。

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の変更にも実行されます。

アイデアはkey、URLが変更されたときにコンポーネントのプロパティを変更することであり、これにより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 )
  }

そして、2回目以降に呼び出されるcomponentWillReceivePropsから、同じルートで異なるIDをロードし、最初のメソッドを呼び出して状態をリロードすると、コンポーネントが新しいアイテムをロードします。

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

これは機能しますが、データをロードしているすべてのコンポーネントへの再レンダリングを処理するために、componentWillReceiveProps()を追加する必要があるという問題があります。
JuhaSyrjälä19年

componentDidUpdate(prevProps)を使用します。componentWillUpdate()componentWillReceiveProps()は推奨されません。
Mirek Michalak

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

componentDidUpdateは非推奨になりました。代替案も提案できるとよいでしょう。
Sahil

よろしいですか?ComponentDidUpdateは今後のバージョンで廃止されなくなりますが、リンクを添付できますか?次のように廃止予定の関数:componentWillMount — UNSAFE_componentWillMount componentWillReceiveProps — UNSAFE_componentWillReceiveProps componentWillUpdate — UNSAFE_componentWillUpdate
Angel Mas

0

提供されている方法を使用できます。

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

ルート変更後にコンポーネントが再レンダリングされることが保証されている場合、依存関係として小道具を渡すことができます

最善の方法ではありませんが、ケントCドッドによると、探しているものを処理するには十分です。


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