如何在react-router v4 中使用上下文 api?

IT技术 reactjs react-router
2021-04-13 00:07:38

我正在我的应用程序上尝试使用 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。我该怎么做?

5个回答

看起来<Switch>应该只有<Route><Redirect >组件作为直接子级。来源

我想这就是为什么你的Redirect,你使用不工作ContextBSwitch孩子。

最简单但重复的解决方案可能是将您ContextB作为<Route>您想要的每个孩子传递

注意:这些解决方案假设您像这样分配了 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;

然后将其用作路由:

<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>

使用此解决方案,然后您将上下文与嵌套组件中的渲染props一起使用:

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

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

成功了,非常感谢!现在我可以更好地理解架构了。
2021-05-26 00:07:38
@jones 我同意你的看法,认为这是一个很好的做法,但我想这取决于每个应用程序的目标。
2021-05-31 00:07:38
阅读下面的答案以了解为什么这种方法不会真正按预期工作:stackoverflow.com/a/61824692/4470169
2021-06-19 00:07:38
但是,如果您从 /route2 或 /route3 转到 /route1,则上下文 B 的状态似乎会丢失。似乎通常将根放在某个地方然后在需要它的那些组件中抓取它通常会更好?
2021-06-22 00:07:38

作为对其他人警告,接受的答案并不像您对原始(非工作)概念所期望的那样有效:

// 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正在共享一个上下文,并且期望应该是:

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

对于已接受的解决方案,上述情况均不成立。

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

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

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

感谢您的帮助,我在商店上下文中使用了它并且有效!。(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>
    );
};

简单的解决方案:

<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>
限制自己严格遵循每个用例的 api 实现是不切实际和理想的。这就是为什么您必须创建一个合乎逻辑的工作来实现您的预​​期结果。
2021-05-29 00:07:38
不,不会。如果有多个交换机,我应该在哪个交换机中包含错误路由,以便路由器不会一直显示它?
2021-06-04 00:07:38
由于 Switch 包装器是嵌套的,因此您必须有条件地呈现错误路由处理程序。您可以定义一个自定义函数,循环遍历所有静态路径并返回一个布尔值。如果未找到匹配项,则使用它来有条件地呈现您的错误路由处理程序。即``` { !isMatch() && <Route component={NotFound}} /> ```
2021-06-09 00:07:38
您建议的方法显然是一种臭味的解决方法。实际上,您所说的意思是构建自己的 Switch 实现而不是内置的实现,我认为这是这里唯一可用的选项。
2021-06-15 00:07:38
@impulsgraw 会的。您只需要在路由声明底部的开关包装器内声明错误路由处理程序。
2021-06-16 00:07:38