如何将上下文 api 与反应路由器 v4 一起使用?

Posted

技术标签:

【中文标题】如何将上下文 api 与反应路由器 v4 一起使用?【英文标题】:How to use context api with react router v4? 【发布时间】:2018-10-13 19:39:04 【问题描述】:

我正在我的应用程序上尝试使用 React 16.3 中的新上下文 API 进行一些测试,但我不明白为什么我的重定向永远不起作用。

<ContextA>
  <Switch>
    <Route exact path='/route1' component= Component1  />
    <ContextB>
      <Route exact path='/route2' component= Component2  />
      <Route exact path='/route3' component= Component3  />
    </ContextB>
    <Redirect from='/' to='/route1' />
  </Switch>
</ContextA>

我不想让我的 ContextB 可用于所有路线,只有 2 和 3 条。我该怎么做?

【问题讨论】:

【参考方案1】:

看起来&lt;Switch&gt; 应该只有&lt;Route&gt;&lt;Redirect &gt; 组件作为直接子级。 (source)

我想这就是为什么你的Redirect 不能工作的原因,因为你使用ContextB 作为Switch 孩子。

最简单但重复的解决方案可能是将您的 ContextB 作为您想要的每个 &lt;Route&gt; 的子代:

注意:这些解决方案假设您为 Context 组件分配了默认值,如下所示:const MyContext = React.createContext(defaultValue);

<Route exact path='/route2'>
  <ContextB.Provider>
    <Component1 />
  </ContextB.Provider>
</Route>

您甚至可以为此创建一个ContextRoute 组件:

import React from 'react';
import  Route  from 'react-router-dom';

const ContextRoute = ( contextComponent, component, ...rest ) => 
  const  Provider  = contextComponent;
  const Component = component;

  return (
    <Route ...rest>
      <Provider>
        <Component />
      </Provider>
    </Route>
  );
;

export default ContextRoute;

然后将其用作Route:

<ContextA>
  <Switch>
    <Route exact path='/route1' component= Component1  />
    <ContextRoute exact path='/route2' contextComponent=ContextB component= Component2  />
    <ContextRoute exact path='/route3' contextComponent=ContextB component= Component3  />
    <Redirect from='/' to='/route1' />
  </Switch>
</ContextA>

通过此解决方案,您可以在嵌套组件中将上下文与渲染道具一起使用:

return (
  <ContextB.Consumer>
    value => <div>value</div>
  </ContextB.Consumer>
);

但我们可以想象更多的解决方案,例如 HOC,将上下文值直接传递给路由组件道具等...

【讨论】:

成功了,非常感谢!现在我可以更好地理解架构了。 似乎上下文 B 的状态会丢失,但是如果您从 /route2 或 /route3 转到 /route1。似乎通常最好将根放在某个地方,然后在需要它的那些组件中抓取它? @jones 我同意你的做法,但我想这取决于每个应用的目标。 阅读下面的答案,了解为什么这种方法不能真正按预期工作:***.com/a/61824692/4470169【参考方案2】:

作为对其他人的警告,接受的答案并不真正起作用,正如您对原始(非工作)概念所期望的那样:

// This comes from the original question (doesn't work as-is!)
<ContextA>
  <Switch>
    <Route exact path='/route1' component= Component1  />
      <ContextB>
        <Route exact path='/route2' component= Component2  />
        <Route exact path='/route3' component= Component3  />
      </ContextB>
      <Redirect from='/' to='/route1' />
   </Switch>
</ContextA>

在那里,/route2/route3 共享一个上下文,期望应该是:

    在路由转换之间保持状态。 如果 Component2Component3 更新上下文,则更改应反映给另一个。

对于公认的解决方案,以上都不是真的。

【讨论】:

你说得对,很好。即使您的答案不是一个真正的(也许在已接受的答案中进行评论或编辑会更合适?)。使其工作的一种方法是将 ContextB 提供程序提升到 Switch 组件的上方,并在路线 2 和 3 上使用消费者。我会在有时间时尝试更新答案 我仍在寻找解决方案,但还没有弄清楚。 @Nenu 我对你关于提升上下文提供者的理论感兴趣——你解决过这个问题吗?【参考方案3】:

在路由中使用渲染方法。这将解决您的问题,就像我在我的应用程序中所做的那样。

<Route
  path="/"
  render=(props) => (
    <ContextB>
      <Component2 ...props />
    </ContextB>
  )
/>

【讨论】:

【参考方案4】:

感谢您的帮助,我在商店环境中使用了它并且有效! (createContext 和 useReducer)

import React, createContext, useReducer from "react";
import Reducer from './reducer.js'

const initialState = 
    /* */
;
const Store = (children) => 
    const [state, dispatch] = useReducer(Reducer, initialState);

    return (
        <Context.Provider value=[state, dispatch]>
            children
        </Context.Provider>
    )
;

export const Context = createContext(initialState);
export default Store;

/* 在 App.js*/

const App = () => 

    return (
    <BrowserRouter>
            <Switch>
            <Route exact path="/" render= (props)=> <Store> <Home ...props /> </Store>  />
            <Route exact path="/about" render= (props)=> <Store> <About ...props /> </Store>  />
            <Route exact path="/login" render= (props)=> <Store> <Login ...props /> </Store>  />
            </Switch>

    </BrowserRouter>
    );
;

【讨论】:

【参考方案5】:

简单的解决方案:

<ContextA>
    <Switch>
        <Route exact path='/' render= () => <Redirect to='/route1' />  />
        <Route path='/route1' component= Component1  />
    </Switch>
    <ContextB>
        <Switch>
            <Route path='/route2' component= Component2  />
            <Route path='/route3' component= Component3  />
        </Switch>        
    </ContextB>        
</ContextA>

【讨论】:

@impulsgraw 会的。您只需在路由声明底部的 switch 包装器中声明错误路由处理程序。 不,不会的。如果有多个交换机,我应该在哪个交换机中包含错误路由,这样路由器就不会一直显示它? 由于 Switch 包装器是嵌套的,因此您必须有条件地呈现您的错误路由处理程序。您可以定义一个自定义函数,循环遍历所有静态路径并返回一个布尔值。如果未找到匹配项,则使用它有条件地呈现您的错误路由处理程序。即 ``` !isMatch() && ``` 您建议的方法显然是一种臭名昭著的解决方法。事实上,您所说的意思是制作自己的 Switch 实现而不是内置的实现,我认为这是这里唯一可用的选项。 限制自己严格遵循每个用例的 api 实现是不切实际和理想的。这就是为什么您必须创建一个符合逻辑的工作来实现您的预​​期结果。

以上是关于如何将上下文 api 与反应路由器 v4 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

无法让 Paypal 在上下文中 checkout.js v4 与 REST API 一起使用

如何将反应上下文与反应导航一起使用

如何使反应路由器与静态资产、html5 模式、历史 API 和嵌套路由一起工作?

如何将 React.createContext() 与反应路由器一起使用?

如何将 Github API v4 (graphql) 与 javascript XMLHttpRequest 一起使用

在浏览器上点击刷新后反应路由器 v4 登录检查