Я работаю над реактивным приложением с использованием реактивного маршрутизатора. У меня есть страница проекта с URL-адресом:
myapplication.com/project/unique-project-id
Когда компонент проекта загружается, я инициирую запрос данных для этого проекта из события componentDidMount. Теперь я столкнулся с проблемой, когда, если я переключаюсь напрямую между двумя проектами, только идентификатор изменяется следующим образом ...
myapplication.com/project/982378632
myapplication.com/project/782387223
myapplication.com/project/198731289
componentDidMount не запускается снова, поэтому данные не обновляются. Есть ли еще одно событие жизненного цикла, которое я должен использовать для запуска моего запроса данных, или другую стратегию для решения этой проблемы?
reactjs
react-router
Созвездия
источник
источник
Ответы:
Если ссылка ведет на тот же маршрут с другим параметром, она не перемонтируется, а вместо этого получает новые реквизиты. Итак, вы можете использовать
componentWillReceiveProps(newProps)
функцию и искатьnewProps.params.projectId
.Если вы пытаетесь загрузить данные, я бы рекомендовал получить данные до того, как маршрутизатор обработает совпадение, используя статические методы компонента. Посмотрите этот пример. React Router Mega Demo . Таким образом, компонент загружает данные и автоматически обновляется при изменении параметров маршрута, не полагаясь на них
componentWillReceiveProps
.источник
state
ему. Сначала это немного сбивает с толку, но потом становится немного понятнее.Если вам действительно нужно перемонтировать компонент при изменении маршрута, вы можете передать уникальный ключ ключевому атрибуту вашего компонента (ключ связан с вашим путем / маршрутом). Таким образом, каждый раз, когда маршрут изменяется, ключ также будет меняться, что запускает компонент React для размонтирования / повторного монтирования. Я понял идею из этого ответа
источник
Вот мой ответ, похожий на некоторые из вышеперечисленных, но с кодом.
<Route path="/page/:pageid" render={(props) => ( <Page key={props.match.params.pageid} {...props} />) } />
источник
Вы должны четко понимать, что изменение маршрута не приведет к обновлению страницы, вы должны справиться с этим самостоятельно.
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 .../> } }
источник
Если у вас есть:
<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
изменении.источник
componentWillReceiveProps
На основании ответов @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.
источник
Ответ @ wei отлично работает, но в некоторых ситуациях я считаю, что лучше не устанавливать ключ внутреннего компонента, а сам маршрутизировать. Кроме того, если путь к компоненту статичен, но вы просто хотите, чтобы компонент перемонтировался каждый раз, когда пользователь переходит к нему (возможно, для выполнения api-вызова в componentDidMount ()), удобно установить 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)
источник
Вот как я решил проблему:
Этот метод получает отдельный элемент из 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, который будет вызываться, поскольку во второй раз мы загружаем тот же маршрут, но с другим идентификатором, и я вызываю первый метод для перезагрузки состояния, а затем компонент загружает новый элемент.
componentWillReceiveProps(nextProps) { if (nextProps.match.params.id !== this.props.match.params.id) { const id = nextProps.match.params.id this.loadConstruction( id ); } }
источник
Если вы используете 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 } } }
источник
Вы можете использовать предоставленный метод:
useEffect(() => { // fetch something }, [props.match.params.id])
когда компоненту гарантировано повторное рендеринг после изменения маршрута, вы можете передать реквизиты как зависимость
Не лучший способ, но достаточно хороший, чтобы справиться с тем, что вы ищете, согласно Кенту С. Доддсу : подумайте больше о том, что вы хотите, чтобы произошло.
источник
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
источник