Pracuję nad aplikacją React przy użyciu routera React-router. Mam stronę projektu, która ma następujący adres URL:
myapplication.com/project/unique-project-id
Po załadowaniu komponentu projektu wyzwalam żądanie danych dla tego projektu ze zdarzenia componentDidMount. Mam teraz problem, w którym jeśli przełączę się bezpośrednio między dwoma projektami, zmieni się tylko identyfikator ...
myapplication.com/project/982378632
myapplication.com/project/782387223
myapplication.com/project/198731289
Element componentDidMount nie jest ponownie wyzwalany, więc dane nie są odświeżane. Czy istnieje inne zdarzenie cyklu życia, którego powinienem użyć, aby wywołać moje żądanie danych lub inna strategia rozwiązania tego problemu?
reactjs
react-router
Constellates
źródło
źródło
Odpowiedzi:
Jeśli łącze kieruje do tej samej trasy z tylko innym parametrem, nie jest ponownie montowane, ale zamiast tego otrzymuje nowe właściwości. Możesz więc użyć
componentWillReceiveProps(newProps)
funkcji i poszukaćnewProps.params.projectId
.Jeśli próbujesz załadować dane, polecam pobranie danych, zanim router obsłuży dopasowanie przy użyciu metod statycznych na komponencie. Sprawdź ten przykład. React Router Mega Demo . W ten sposób komponent załadowałby dane i automatycznie zaktualizowałby się, gdy parametry trasy ulegną zmianie, bez konieczności polegania na nich
componentWillReceiveProps
.źródło
state
do niego plik . Na początku jest to trochę zagmatwane, ale potem staje się trochę jaśniejsze.Jeśli potrzebujesz ponownego zamontowania komponentu po zmianie trasy, możesz przekazać unikalny klucz do atrybutu klucza komponentu (klucz jest powiązany ze ścieżką / trasą). Więc za każdym razem, gdy zmienia się trasa, klucz również się zmienia, co wyzwala komponent React do odmontowania / ponownego zamontowania. Wpadłem na pomysł z tej odpowiedzi
źródło
Oto moja odpowiedź, podobna do niektórych z powyższych, ale z kodem.
<Route path="/page/:pageid" render={(props) => ( <Page key={props.match.params.pageid} {...props} />) } />
źródło
Musisz mieć jasność, że zmiana trasy nie spowoduje odświeżenia strony, musisz sobie z tym poradzić.
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 .../> } }
źródło
Jeśli masz:
<Route render={(props) => <Component {...props} />} path="/project/:projectId/" />
W React 16.8 i nowszych, używając hooków , możesz:
import React, { useEffect } from "react"; const Component = (props) => { useEffect(() => { props.fetchResource(); }, [props.match.params.projectId]); return (<div>Layout</div>); } export default Component;
Tam wywołujesz nowe
fetchResource
połączenie tylko wtedy, gdy sięprops.match.params.id
zmieni.źródło
componentWillReceiveProps
Na podstawie odpowiedzi @wei, @ Breakpoint25 i @PaulusLimma stworzyłem ten komponent zastępczy dla
<Route>
. Spowoduje to ponowne zamontowanie strony, gdy zmieni się adres URL, wymuszając ponowne utworzenie i zamontowanie wszystkich komponentów na stronie, a nie tylko ponowne renderowanie. WszystkiecomponentDidMount()
inne haki startowe są wykonywane również przy zmianie adresu URL.Pomysł polega na zmianie
key
właściwości komponentów, gdy zmieni się adres URL, co zmusza Reacta do ponownego zamontowania komponentu.Możesz go użyć jako zamiennika typu drop-in
<Route>
, na przykład w ten sposób:<Router> <Switch> <RemountingRoute path="/item/:id" exact={true} component={ItemPage} /> <RemountingRoute path="/stuff/:id" exact={true} component={StuffPage} /> </Switch> </Router>
<RemountingRoute>
Składowa jest zdefiniowana w następujący sposób: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 }
Zostało to przetestowane z React-Router 4.3.
źródło
Odpowiedź @ wei działa świetnie, ale w niektórych sytuacjach uważam, że lepiej nie ustawiać klucza komponentu wewnętrznego, ale samą trasę. Ponadto, jeśli ścieżka do komponentu jest statyczna, ale chcesz, aby komponent był montowany ponownie za każdym razem, gdy użytkownik do niego przechodzi (na przykład w celu wykonania wywołania api w componentDidMount ()), przydatne jest ustawienie location.pathname na klucz trasy. Dzięki niemu trasa i cała jej zawartość zostanie ponownie zamontowana, gdy zmieni się lokalizacja.
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)
źródło
Oto jak rozwiązałem problem:
Ta metoda pobiera pojedynczy element z interfejsu API:
loadConstruction( id ) { axios.get('/construction/' + id) .then( construction => { this.setState({ construction: construction.data }) }) .catch( error => { console.log('error: ', error); }) }
Wywołuję tę metodę z componentDidMount, ta metoda zostanie wywołana tylko raz, kiedy załaduję tę trasę po raz pierwszy:
componentDidMount() { const id = this.props.match.params.id; this.loadConstruction( id ) }
I z componentWillReceiveProps, które będą wywoływane od drugiego razu, ładujemy tę samą trasę, ale inny identyfikator, i wywołuję pierwszą metodę, aby ponownie załadować stan, a następnie komponent załaduje nowy element.
componentWillReceiveProps(nextProps) { if (nextProps.match.params.id !== this.props.match.params.id) { const id = nextProps.match.params.id this.loadConstruction( id ); } }
źródło
Jeśli używasz komponentu klasy, możesz użyć komponentu 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 } } }
źródło
Możesz skorzystać z podanej metody:
useEffect(() => { // fetch something }, [props.match.params.id])
kiedy gwarantowane jest ponowne renderowanie komponentu po zmianie trasy, możesz przekazać właściwości jako zależność
Według Kent C. Dodds nie jest to najlepszy sposób, ale wystarczająco dobry, aby poradzić sobie z tym, czego szukasz : Zastanów się bardziej, co chcesz, aby się wydarzyło.
źródło
react-router
jest uszkodzony, ponieważ musi ponownie montować komponenty przy każdej zmianie lokalizacji.Udało mi się jednak znaleźć poprawkę dla tego błędu:
https://github.com/ReactTraining/react-router/issues/1982#issuecomment-275346314
W skrócie (zobacz powyższy link, aby uzyskać pełne informacje)
<Router createElement={ (component, props) => { const { location } = props const key = `${location.pathname}${location.search}` props = { ...props, key } return React.createElement(component, props) } }/>
Spowoduje to ponowne zamontowanie przy każdej zmianie adresu URL
źródło