Komponen tidak dipasang kembali saat parameter rute berubah

100

Saya sedang mengerjakan aplikasi react menggunakan react-router. Saya memiliki halaman proyek yang memiliki url sebagai berikut:

myapplication.com/project/unique-project-id

Ketika komponen proyek dimuat, saya memicu permintaan data untuk proyek itu dari acara componentDidMount. Saya sekarang mengalami masalah di mana jika saya beralih langsung di antara dua proyek jadi hanya id yang berubah seperti ini ...

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

componentDidMount tidak dipicu lagi sehingga data tidak di-refresh. Apakah ada peristiwa siklus hidup lain yang harus saya gunakan untuk memicu permintaan data saya atau strategi lain untuk mengatasi masalah ini?

Constellates
sumber
1
Bisakah Anda memposting kode komponen Anda?
Pierre Criulanscy

Jawaban:

81

Jika tautan mengarah ke rute yang sama hanya dengan parameter yang berbeda, itu tidak memasang ulang, melainkan menerima properti baru. Jadi, Anda bisa menggunakan componentWillReceiveProps(newProps)fungsi tersebut dan mencari newProps.params.projectId.

Jika Anda mencoba memuat data, saya sarankan untuk mengambil data sebelum router menangani pertandingan menggunakan metode statis pada komponen. Lihat contoh ini. React Router Mega Demo . Dengan cara itu, komponen akan memuat data dan secara otomatis memperbarui ketika parameter rute berubah tanpa perlu bergantung componentWillReceiveProps.

Brad B
sumber
2
Terima kasih. Saya bisa mendapatkan ini bekerja menggunakan componentWillReceiveProps. Saya masih tidak terlalu memahami aliran data Demo React Router Mega. Saya melihat fetchData statis ditentukan pada beberapa komponen. Bagaimana statika ini dipanggil dari sisi klien router?
Konstelasi
1
Pertanyaan bagus. Lihat file client.js dan file server.js. Selama runcycle router, mereka memanggil file fetchData.js dan meneruskannya state. Ini sedikit membingungkan pada awalnya, tetapi kemudian menjadi sedikit lebih jelas.
Brad B
12
FYI: "componentWillReceiveProps" dianggap warisan
Idan Dagan
4
Anda harus menggunakan ComponentDidUpdate dengan React 16 karena componentWillReceiveProps tidak digunakan lagi
Pato Loco
1
Ini berfungsi tetapi memiliki masalah sehingga Anda perlu menambahkan componentWillReceiveProps () atau componentDidUpdate () untuk menangani rendering ulang ke setiap komponen yang memuat data. Misalnya pikirkan tentang halaman yang memiliki beberapa komponen yang memuat data berdasarkan parameter jalur yang sama.
Juha Syrjälä
99

Jika Anda memang membutuhkan komponen yang dipasang kembali saat rute berubah, Anda bisa meneruskan kunci unik ke atribut kunci komponen Anda (kunci tersebut terkait dengan jalur / rute Anda). Jadi setiap kali route berubah, kuncinya juga akan berubah yang memicu komponen React untuk unmount / remount. Saya mendapat ide dari jawaban ini

wei
sumber
2
Cemerlang! Terima kasih atas jawaban sederhana dan elegan ini
Z_z_Z
Hanya bertanya, bukankah ini akan memengaruhi kinerja aplikasi?
Alpit Anand
1
@AlpitAnand ini pertanyaan yang luas. Memasang ulang jelas lebih lambat daripada rendering ulang karena memicu lebih banyak metode siklus hidup. Di sini saya hanya menjawab bagaimana membuat remount terjadi ketika rute berubah.
wei
Tapi itu bukan dampak yang besar? Apa saran Anda, apakah kami dapat menggunakannya dengan aman tanpa banyak efek?
Alpit Anand
@AlpitAnand Pertanyaan Anda terlalu luas dan spesifik untuk aplikasi. Untuk beberapa aplikasi, ya, ini akan menjadi hit kinerja, dalam hal ini Anda harus menonton alat peraga mengubah diri Anda sendiri dan hanya memperbarui elemen yang harus diperbarui. Namun untuk sebagian besar, di atas adalah solusi yang baik
Chris
85

Inilah jawaban saya, mirip dengan beberapa di atas tetapi dengan kode.

<Route path="/page/:pageid" render={(props) => (
  <Page key={props.match.params.pageid} {...props} />)
} />
Titik putus 25
sumber
4
Kuncinya di sini memainkan peran terbesar, karena gambar yang Anda tautkan di halaman profil, setelah diklik, langsung dialihkan ke rute yang sama, tetapi dengan parameter yang berbeda, TETAPI komponen tidak diperbarui, karena React tidak dapat menemukan perbedaan, karena harus ada KUNCI untuk membandingkan hasil lama
Georgi Peev
15

Anda harus jelas, bahwa perubahan rute tidak akan menyebabkan halaman disegarkan, Anda harus menanganinya sendiri.

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 .../> }
}
ayam jago
sumber
Terima kasih, solusi ini berhasil untuk saya. Tetapi pengaturan eslint saya mengeluhkan hal ini: github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/… Apa pendapat Anda tentang ini?
konekoya
Ya, itu sebenarnya bukan praktik terbaik, maaf tentang itu, setelah mengirimkan "fetchProject" peredam Anda akan memperbarui alat peraga Anda, saya hanya menggunakan ini untuk menyampaikan maksud saya tanpa menempatkan
reduksinya
Hai chickenchilli, Sebenarnya saya masih terjebak dalam hal ini ... apakah Anda punya saran atau tutorial lain yang direkomendasikan? Atau saya akan tetap berpegang pada solusi Anda untuk saat ini. Terima kasih atas bantuan Anda!
konekoya
13

Jika Anda memiliki:

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

Di React 16.8 dan yang lebih baru, menggunakan hook , Anda dapat melakukan:

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

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

Di sana, Anda hanya memicu fetchResourcepanggilan baru setiap kali ada props.match.params.idperubahan.

Alfonso Pérez
sumber
4
Ini adalah jawaban yang lebih baik mengingat sebagian besar jawaban lain bergantung pada jawaban yang sekarang sudah usang dan tidak amancomponentWillReceiveProps
pjb
8

Berdasarkan jawaban @wei, @ Breakpoint25 dan @PaulusLimma, saya membuat komponen pengganti ini untuk <Route>. Ini akan memasang ulang halaman saat URL berubah, memaksa semua komponen di halaman untuk dibuat dan dipasang lagi, tidak hanya dirender ulang. Semua componentDidMount()dan semua pengait startup lainnya juga dijalankan pada perubahan URL.

Idenya adalah untuk mengubah keyproperti komponen ketika URL berubah dan ini memaksa React untuk memasang kembali komponen tersebut.

Anda dapat menggunakannya sebagai pengganti drop-in <Route>, misalnya seperti ini:

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

The <RemountingRoute>komponen didefinisikan seperti ini:

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
}

Ini telah diuji dengan React-Router 4.3.

Juha Syrjälä
sumber
5

Jawaban @ wei berfungsi dengan baik, tetapi dalam beberapa situasi saya merasa lebih baik untuk tidak menetapkan kunci komponen dalam, tetapi merutekan itu sendiri. Selain itu, jika jalur ke komponen statis, tetapi Anda hanya ingin komponen dipasang ulang setiap kali pengguna menavigasi ke sana (mungkin untuk membuat panggilan api di componentDidMount ()), sebaiknya setel location.pathname ke kunci rute. Dengan itu rute dan semua isinya akan dipasang kembali ketika lokasi berubah.

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)
Paulus Limma
sumber
5

Beginilah cara saya memecahkan masalah:

Metode ini mendapatkan item individu dari API:

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

Saya memanggil metode ini dari componentDidMount, metode ini hanya akan dipanggil sekali, ketika saya memuat rute ini untuk pertama kalinya:

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

Dan dari componentWillReceiveProps yang akan dipanggil sejak kedua kalinya kita memuat rute yang sama tetapi ID berbeda, dan saya memanggil metode pertama untuk memuat ulang status dan kemudian komponen akan memuat item baru.

componentWillReceiveProps(nextProps) {
    if (nextProps.match.params.id !== this.props.match.params.id) {
      const id = nextProps.match.params.id
      this.loadConstruction( id );
    }
  }
Oscar Jovanny
sumber
Ini berfungsi tetapi memiliki masalah sehingga Anda perlu menambahkan componentWillReceiveProps () untuk menangani render ulang ke setiap komponen yang memuat data.
Juha Syrjälä
Gunakan componentDidUpdate (prevProps). componentWillUpdate () componentWillReceiveProps () tidak digunakan lagi.
Mirek Michalak
0

Jika Anda menggunakan Komponen Kelas, Anda dapat menggunakan 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
      }
    }
 }
Angel Mas
sumber
componentDidUpdate tidak digunakan lagi sekarang, alangkah baiknya jika Anda juga dapat menyarankan beberapa alternatif.
Sahil
Apa kamu yakin akan hal itu? ComponentDidUpdate tidak dan tidak akan digunakan lagi dalam versi yang akan datang, dapatkah Anda melampirkan beberapa tautan? fungsi yang akan dihentikan sebagai berikut: componentWillMount - UNSAFE_componentWillMount componentWillReceiveProps - UNSAFE_componentWillReceiveProps componentWillUpdate - UNSAFE_componentWillUpdate
Angel Mas
0

Anda dapat menggunakan metode yang disediakan:

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

Jika komponen dijamin akan dirender ulang setelah perubahan rute, Anda dapat meneruskan props sebagai dependensi

Bukan cara terbaik tapi cukup baik untuk menangani apa yang Anda cari menurut Kent C Dodds : Pertimbangkan lebih banyak apa yang Anda inginkan terjadi.

Vince Sanchez Tañan
sumber
-3

react-router rusak karena harus memasang kembali komponen pada setiap perubahan lokasi.

Saya berhasil menemukan perbaikan untuk bug ini:

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

Singkatnya (lihat tautan di atas untuk info lengkap)

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

Ini akan membuatnya dipasang kembali pada setiap perubahan URL

catamphetamine
sumber