React Context API + withRouter - 我们可以一起使用它们吗?
React Context API + withRouter - can we use them together?
我构建了一个大型应用程序,其中导航栏上的单个按钮可打开模式。
我正在使用上下文 API 跟踪 modalOpen 状态。
因此,用户单击导航栏上的按钮。模态打开。 Modal 有一个名为 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 Warning: withRouter(QuoteCalculator): Function
components do not support contextType.
我很想忽略这个警告,但我认为这不是个好主意。
我试过使用重定向。所以像
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;
这种方法的问题是我一直收到错误
You tried to redirect to the same route you're currently on
此外,我宁愿不使用这种方法,只是因为那样我必须撤消完成状态(否则当用户再次单击按钮时,完成为真,我们将被重定向).. .
知道 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>
);
}
}
警告来源
与大多数高阶组件不同,withRouter
将您传递的组件包装在功能组件而不是 class 组件中。但它仍在调用 hoistStatics
,这会将您的 contextType
静态化并将其移动到 withRouter
返回的函数组件。这通常应该没问题,但您发现了一个情况并非如此。您可以查看存储库 code 了解更多详细信息,但它很短,所以我将在这里为您删除相关行:
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
中以访问路由器属性。
不错
您还可以使模态组件成为模态 content/functionality 的简单包装器,使用 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')
// ..
}
// ...
}
我构建了一个大型应用程序,其中导航栏上的单个按钮可打开模式。
我正在使用上下文 API 跟踪 modalOpen 状态。
因此,用户单击导航栏上的按钮。模态打开。 Modal 有一个名为 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 Warning: withRouter(QuoteCalculator): Function components do not support contextType.
我很想忽略这个警告,但我认为这不是个好主意。
我试过使用重定向。所以像
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;
这种方法的问题是我一直收到错误
You tried to redirect to the same route you're currently on
此外,我宁愿不使用这种方法,只是因为那样我必须撤消完成状态(否则当用户再次单击按钮时,完成为真,我们将被重定向).. .
知道 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>
);
}
}
警告来源
与大多数高阶组件不同,withRouter
将您传递的组件包装在功能组件而不是 class 组件中。但它仍在调用 hoistStatics
,这会将您的 contextType
静态化并将其移动到 withRouter
返回的函数组件。这通常应该没问题,但您发现了一个情况并非如此。您可以查看存储库 code 了解更多详细信息,但它很短,所以我将在这里为您删除相关行:
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
中以访问路由器属性。
不错
您还可以使模态组件成为模态 content/functionality 的简单包装器,使用 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')
// ..
}
// ...
}