React Context API + withRouter - можем ли мы использовать их вместе?
Я построил большое приложение, в котором одна кнопка на панели навигации открывает модал.
Я отслеживаю состояние modalOpen, используя контекстный API.
Итак, пользователь нажимает кнопку на панели навигации. Модал Открывается. Модал имеет контейнер под названием QuoteCalculator.
QuoteCalculator выглядит следующим образом:
class QuoteCalculator extends React.Component {
static contextType = ModalContext;
// ...
onSubmit = () => {
// ...
this.context.toggleModal();
this.props.history.push('/quote');
// ..
};
render() {
//...
return(<Question {...props} next={this.onSubmit} />;)
}
}
export default withRouter(QuoteCalculator);
Теперь все работает как положено. Когда пользователь отправляет, я иду по правильному маршруту. Я просто вижу следующее предупреждение на консоли
index.js:1446 Предупреждение: withRouter(QuoteCalculator): Компоненты функций не поддерживают contextType.
Я склонен игнорировать предупреждение, но я не думаю, что это хорошая идея.
Я попытался использовать Redirect в качестве альтернативы. Так что-то вроде
QuoteCalculator looks as follows:
class QuoteCalculator extends React.Component {
static contextType = ModalContext;
// ...
onSubmit = () => {
// ...
this.context.toggleModal();
this.setState({done: true});
// ..
};
render() {
let toDisplay;
if(this.state.done) {
toDisplay = <Redirect to="/quote"/>
} else {
toDipslay = <Question {...props} next={this.onSubmit} />;
}
return(<>{toDisplay}</>)
}
}
export default QuoteCalculator;
Проблема с этим подходом состоит в том, что я продолжал получать ошибку
Вы пытались перенаправить на тот же маршрут, на котором находитесь
Кроме того, я бы предпочел не использовать этот подход, просто потому, что тогда мне пришлось бы отменить состояние "сделано" (в противном случае, когда пользователь снова нажимает кнопку "Готово", значение "истина", и мы просто будем перенаправлены)...
Любая идея, что происходит с withRouter и history.push?
Вот мое приложение
class App extends Component {
render() {
return (
<Layout>
<Switch>
<Route path="/quote" component={Quote} />
<Route path="/pricing" component={Pricing} />
<Route path="/about" component={About} />
<Route path="/faq" component={FAQ} />
<Route path="/" exact component={Home} />
<Redirect to="/" />
</Switch>
</Layout>
);
}
}
1 ответ
Источник предупреждения
В отличие от большинства компонентов более высокого порядка, withRouter
оборачивает компонент, который вы передаете, в функциональный компонент вместо компонента класса. Но это все еще зовет hoistStatics
, который принимает ваш contextType
статическое и перемещение его в компонент функции, возвращаемый withRouter
, Обычно это должно быть хорошо, но вы нашли случай, когда это не так. Вы можете проверить код репо для более подробной информации, но он короткий, поэтому я просто опущу соответствующие строки для вас:
function withRouter(Component) {
// this is a functional component
const C = props => {
const { wrappedComponentRef, ...remainingProps } = props;
return (
<Route
children={routeComponentProps => (
<Component
{...remainingProps}
{...routeComponentProps}
ref={wrappedComponentRef}
/>
)}
/>
);
};
// ...
// hoistStatics moves statics from Component to C
return hoistStatics(C, Component);
}
Это действительно не должно негативно влиять на что-либо. Ваш контекст будет по-прежнему работать и будет просто игнорироваться в компоненте переноса, возвращенном из withRouter
, Тем не менее, это не сложно изменить вещи, чтобы устранить эту проблему.
Возможные решения
самыйпростой
Так как все, что вам нужно в вашем модале, это history.push
Вы можете просто передать это как реквизит из родительского компонента модала. Учитывая настройки, которые вы описали, я предполагаю, что модал включен в одно место в приложении, довольно высоко в дереве компонентов. Если компонент, включающий ваш модал, уже Route
компонент, то он имеет доступ к history
и может просто пройти push
вместе с модальным. Если это не так, то оберните родительский компонент в withRouter
получить доступ к реквизиту роутера.
Неплохо
Вы также можете сделать ваш модальный компонент простой оболочкой для вашего модального контента / функциональности, используя ModalContext.Consumer
компонент для передачи необходимого контекста в качестве реквизита вместо использования contextType
,
const Modal = () => (
<ModalContext.Consumer>
{value => <ModalContent {...value} />}
</ModalContext.Consumer>
)
class ModalContent extends React.Component {
onSubmit = () => {
// ...
this.props.toggleModal()
this.props.history.push('/quote')
// ..
}
// ...
}