Rota parametreleri değiştiğinde bileşen yeniden bağlanmıyor


104

React-router kullanan bir react uygulaması üzerinde çalışıyorum. Aşağıdaki gibi bir url'ye sahip bir proje sayfam var:

myapplication.com/project/unique-project-id

Proje bileşeni yüklendiğinde, componentDidMount olayından o proje için bir veri talebini tetiklerim. Şimdi, iki proje arasında doğrudan geçiş yaparsam, böylece yalnızca kimlik böyle değişirse bir sorunla karşılaşıyorum ...

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

componentDidMount tekrar tetiklenmediğinden veriler yenilenmez. Veri talebimi tetiklemek için kullanmam gereken başka bir yaşam döngüsü olayı veya bu sorunu çözmek için farklı bir strateji var mı?


1
Bileşen kodunuzu gönderebilir misiniz?
Pierre Criulanscy

Yanıtlar:


83

Bağlantı sadece farklı bir parametre ile aynı rotaya yönleniyorsa, bu yeniden bağlanmak yerine yeni sahne alanlarını alır. Yani, kullanabilirsinizcomponentWillReceiveProps(newProps) işlevi ve arayabilirsiniz newProps.params.projectId.

Verileri yüklemeye çalışıyorsanız, yönlendirici eşleşmeyi bileşende statik yöntemler kullanarak işlemeden önce verileri getirmenizi tavsiye ederim. Bu örneğe bakın. React Router Mega Demo . Bu şekilde, bileşen verileri yükler ve güvenmeye gerek kalmadan rota parametreleri değiştiğinde otomatik olarak güncellenir componentWillReceiveProps.


2
Teşekkür ederim. Bu çalışmayı componentWillReceiveProps kullanarak elde edebildim. Hala React Router Mega Demo veri akışını gerçekten anlamıyorum. Bazı bileşenlerde tanımlanan statik fetchData'yı görüyorum. Bu statikler yönlendirici istemci tarafından nasıl çağrılıyor?
Constellates

1
Harika soru. Client.js dosyasını ve server.js dosyasını kontrol edin. Yönlendiricinin çalışma döngüsü sırasında, fetchData.js dosyasını çağırırlar ve ona iletirler state. İlk başta biraz kafa karıştırıcı ama sonra biraz daha netleşiyor.
Brad B

13
Bilginize: "componentWillReceiveProps" miras olarak kabul edildi
Idan Dagan

5
ComponentDidUpdate'i React 16 ile birlikte kullanmalısınız çünkü componentWillReceiveProps kullanımdan kaldırılmıştır
Pato Loco

1
Bu işe yarıyor, ancak veri yükleyen her bileşene yeniden oluşturmaları işlemek için componentWillReceiveProps () veya componentDidUpdate () eklemeniz gereken bir soruna sahip. Örneğin, aynı yol parametresine göre veri yükleyen birkaç bileşene sahip bir sayfayı düşünün.
Juha Syrjälä

104

Rota değiştiğinde bir bileşenin yeniden bağlanmasına ihtiyacınız varsa, bileşeninizin anahtar özelliğine benzersiz bir anahtar iletebilirsiniz (anahtar, yolunuzla / rotanızla ilişkilendirilir). Bu nedenle, rota her değiştiğinde, anahtar aynı zamanda React bileşenini ayırmak / yeniden bağlamak için tetikleyen anahtarı da değiştirecektir. Fikri bu cevaptan aldım


3
Parlak! Bu basit ve zarif cevap için teşekkür ederiz
Z_z_Z

Sadece soruyorum, bu uygulamanın performansını etkilemeyecek mi?
Alpit Anand

1
@AlpitAnand bu geniş bir sorudur. Daha fazla yaşam döngüsü yöntemini tetiklediği için yeniden montaj, yeniden oluşturmadan kesinlikle daha yavaştır. Burada, rota değiştiğinde yeniden saymanın nasıl yapılacağını yanıtlıyorum.
wei

Ama bu çok büyük bir etki değil mi? Tavsiyeniz çok fazla etki yaratmadan güvenle kullanabilir miyiz?
Alpit Anand

1
@AlpitAnand Sorunuz çok geniş ve uygulamaya özel. Bazı uygulamalar için, evet, bu bir performans vuruşu olur, bu durumda kendinizi değiştiren sahne öğelerini izlemeli ve yalnızca güncellenmesi gereken öğeleri güncellemelisiniz. Yine de çoğu için, yukarıdakiler iyi bir çözüm
Chris

88

İşte cevabım, yukarıdakilerden bazılarına benzer ancak kodlu.

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

4
Buradaki anahtar en büyük rolü oynar, çünkü bir profil sayfasında Bağlantınız olan görüntü, tıkladığınızda sadece aynı rotaya yönlendirir, ancak farklı parametrelerle, ANCAK bileşen güncellenmez, çünkü React bir fark bulamaz. çünkü eski sonucu karşılaştırmak için bir ANAHTAR olmalı
Georgi Peev

15

Bir rota değişikliğinin sayfanın yenilenmesine neden olmayacağından emin olmalısınız, bunu kendiniz halletmelisiniz.

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

Teşekkürler, bu çözüm benim için çalışıyor. Ancak eslint ayarım bundan şikayet ediyor: github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/… Bunun hakkında ne düşünüyorsunuz?
konekoya

Evet, bu aslında en iyi uygulama değil, bunun için üzgünüm, "fetchProject" i gönderdikten sonra redüktörünüz yine de sahne donanımınızı güncelleyecektir, bunu
redux'u

Merhaba chickenchilli, Aslında hala bu konuda sıkıştım ... başka önerileriniz veya önerilen dersleriniz var mı? Ya da şimdilik çözümünüze bağlı kalacağım. Yardımınız için teşekkürler!
konekoya

14

Eğer varsa:

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

React 16.8 ve üzeri sürümlerde, kancaları kullanarak şunları yapabilirsiniz:

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

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

Orada, yalnızca fetchResourceher props.match.params.iddeğiştiğinde yeni bir aramayı tetikliyorsunuz .


4
Diğer cevapların çoğunun artık kullanımdan kaldırılan ve güvensiz componentWillReceiveProps
olana

10

@Wei, @ Breakpoint25 ve @PaulusLimma tarafından verilen yanıtlara göre <Route>. Bu, URL değiştiğinde sayfayı yeniden bağlar ve sayfadaki tüm bileşenleri yalnızca yeniden oluşturmayı değil, yeniden oluşturulmaya ve bağlanmaya zorlar. Tüm componentDidMount()diğer başlangıç ​​kancaları da URL değişikliğinde yürütülür.

Buradaki fikir, keyURL değiştiğinde bileşen özelliğini değiştirmektir ve bu, React'i bileşeni yeniden monte etmeye zorlar.

Bunu, <Route>örneğin bunun gibi bir drop-in yerine kullanabilirsiniz :

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

<RemountingRoute>Bileşeni aşağıdaki gibi tanımlanır:

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
}

Bu, React-Router 4.3 ile test edilmiştir.


5

@ Wei'nin cevabı harika çalışıyor, ancak bazı durumlarda iç bileşenin bir anahtarını belirlemekten ziyade kendisini yönlendirmeyi daha iyi buluyorum. Ayrıca, bileşene giden yol statikse, ancak yalnızca kullanıcının ona her gittiğinde yeniden bağlanmasını istiyorsanız (belki componentDidMount () 'da bir api-çağrısı yapmak için), location.pathname'i yolun anahtarına ayarlamak kullanışlıdır. Onunla rota ve tüm içeriği konum değiştiğinde yeniden monte edilir.

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

Sorunu şu şekilde çözdüm:

Bu yöntem, bağımsız öğeyi API'den alır:

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

Bu yöntemi componentDidMount'tan çağırıyorum, bu yöntemi ilk kez yüklediğimde bu yöntem yalnızca bir kez çağrılacak:

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

Ve ikinci seferden beri çağrılacak olan componentWillReceiveProps'tan aynı rotayı ancak farklı ID'yi yüklüyoruz ve durumu yeniden yüklemek için ilk yöntemi çağırıyorum ve sonra bileşen yeni öğeyi yükleyecektir.

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

Bu çalışır, ancak veri yükleyen her bileşene yeniden işlemeleri işlemek için componentWillReceiveProps () eklemeniz gereken bir soruna sahiptir.
Juha Syrjälä

ComponentDidUpdate (prevProps) kullanın. componentWillUpdate () componentWillReceiveProps () kullanımdan kaldırıldı.
Mirek Michalak

0

Sınıf Bileşeni kullanıyorsanız, componentDidUpdate'i kullanabilirsiniz.

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 şu anda kullanımdan kaldırıldı, siz de bir alternatif önerebilirseniz iyi olur.
Sahil

Bundan emin misin? ComponentDidUpdate gelecek sürümde kullanımdan kaldırılmayacak ve kaldırılmayacak, bir bağlantı ekleyebilir misiniz? kullanımdan kaldırılacak işlevler aşağıdaki gibidir: componentWillMount - UNSAFE_componentWillMount componentWillReceiveProps - UNSAFE_componentWillReceiveProps componentWillUpdate - UNSAFE_componentWillUpdate
Angel Mas

0

Sağlanan yöntemi kullanabilirsiniz:

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

rota değişikliğinden sonra bileşenin yeniden işlenmesi garanti edildiğinde, bağımlılık olarak props'ı geçebilirsiniz

En iyi yol değil ama Kent C Dodds'a göre aradığınızı halledecek kadar iyi : Ne olmasını istediğinizi daha fazla düşünün.


-3

react-router herhangi bir konum değişikliğinde bileşenleri yeniden monte etmesi gerektiğinden bozuk.

Yine de bu hata için bir düzeltme bulmayı başardım:

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

Kısaca (tüm bilgiler için yukarıdaki bağlantıya bakın)

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

Bu, herhangi bir URL değişikliğinde yeniden bağlanmasını sağlayacaktır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.