如何通过 React Router 传递状态?

Posted

技术标签:

【中文标题】如何通过 React Router 传递状态?【英文标题】:How do I pass state through React_router? 【发布时间】:2017-05-18 21:01:28 【问题描述】:

这是给我带来麻烦的文件:

var Routers = React.createClass(

  getInitialState: function()
    return
      userName: "",
      relatives: []
    
  ,

  userLoggedIn: function(userName, relatives)
    this.setState(
      userName: userName,
      relatives: relatives,
    )
  ,

  render: function() 
    return (
      <Router history=browserHistory>
        <Route path="/" userLoggedIn=this.userLoggedIn component=LogIn/>
        <Route path="feed" relatives=this.state.relatives userName=this.state.userName component=Feed/>
      </Router>
    );
  
);

我正在尝试通过路由将新的this.state.relativesthis.state.userName 传递到我的“feed”组件中。但我收到此错误消息:

警告:[react-router] 你不能改变;这将是 忽略

我知道为什么会发生这种情况,但不知道我应该如何将状态传递给我的“提要”组件。在过去的 5 个小时里,我一直在尝试解决这个问题,我变得非常绝望!

请帮忙! 谢谢


解决方案:

下面的答案很有帮助,我感谢作者,但他们不是最简单的方法。 在我的情况下,最好的方法是这样的: 当您更改路线时,您只需像这样附加一条消息:

browserHistory.push(pathname: '/pathname', state: message: "hello, im a passed message!");

或者如果您通过链接进行操作:

<Link 
    to= 
    pathname: '/pathname', 
    state:  message: 'hello, im a passed message!'  
  />

来源:https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/location.md

在您尝试访问的组件中,您可以像这样访问变量,例如:

  componentDidMount: function() 
    var recievedMessage = this.props.location.state.message
  ,

我希望这会有所帮助! :)

【问题讨论】:

查看此答案***.com/a/29720488/444829 &lt;Route&gt; 来自旧版本,因此您将继续使用component 属性,而不是该答案使用的handler 属性。 会写一个带有解释的答案。 nononono 不要!我已经找到了解决方案!我在找到解决方案后立即删除了我所做的评论,但不幸的是,你似乎已经把它变红了。非常感谢:) @PaulS 再次想到我一直在努力学习,而我发现的解决方案似乎只是一个半解决方案。你介意解释一下你提到的解决方案在我的场景中会是什么样子吗?真的很棒! :) 随时将您更新的解决方案作为答案,然后将您的答案标记为正确答案。 【参考方案1】:

tl;dr 在管理需要在整个应用程序中访问的状态时,最好的办法是使用像 reduxmobx 这样的存储。这些库允许您的组件连接/观察状态并随时了解任何状态更改。

什么是&lt;Route&gt;

你不能通过 &lt;Route&gt; 组件传递 props 的原因是它们不是真正的组件,因为它们不渲染任何东西。相反,它们用于构建路由配置对象。

这意味着:

<Router history=browserHistory>
  <Route path='/' component=App>
    <Route path='foo' component=Foo />
  </Route>
</Router>

等价于:

<Router history=browserHistory routes=
  path: '/',
  component: App,
  childRoutes: [
    
      path: 'foo',
      component: Foo
    
  ]
 />

路线仅在初始装载时评估,这就是为什么您不能将新道具传递给它们的原因。

静态道具

如果您有一些静态道具想要传递给您的商店,您可以创建自己的高阶组件,将它们注入商店。不幸的是,这仅适用于静态道具,因为如上所述,&lt;Route&gt;s 仅被评估一次。

function withProps(Component, props) 
  return function(matchProps) 
    return <Component ...props ...matchProps />
  


class MyApp extends React.Component 
  render() 
    return (
      <Router history=browserHistory>
        <Route path='/' component=App>
          <Route path='foo' component=withProps(Foo,  test: 'ing' ) />
        </Route>
      </Router>
    )
  

使用location.state

location.state 是在导航时在组件之间传递状态的便捷方式。然而,它有一个主要的缺点,那就是状态仅在您的应用程序中导航时存在。如果用户点击指向您网站的链接,则该位置不会附加任何状态。

使用商店

那么如何将数据传递给路由的components?一种常见的方法是使用像reduxmobx 这样的商店。使用redux,您可以使用更高阶的组件将您的组件connect 存储到商店。然后,当你的路由的component(它实际上是以你的路由组件作为其子组件的 HOC)渲染时,它可以从存储中获取最新信息。

const Foo = (props) => (
  <div>props.username</div>
)

function mapStateToProps(state) 
  return 
    value: state.username
  ;


export default connect(mapStateToProps)(Foo)

我对@9​​87654340@ 不是特别熟悉,但据我了解,它可以更容易设置。使用 reduxmobx 或其他状态管理之一是在整个应用程序中传递状态的好方法。

注意:您可以在此处停止阅读。以下是传递状态的合理示例,但您可能应该只使用存储库。

没有商店

如果您不想使用商店怎么办?你运气不好?不可以,但您必须使用 React 的实验性功能context。为了使用context,您的父组件之一必须显式定义getChildContext 方法以及childContextTypes 对象。任何想要通过context 访问这些值的子组件都需要定义一个contextTypes 对象(类似于propTypes)。

class MyApp extends React.Component 

  getChildContext() 
    return 
      username: this.state.username
    
  



MyApp.childContextTypes = 
  username: React.PropTypes.object


const Foo = (props, context) => (
  <div>context.username</div>
)

Foo.contextTypes = 
  username: React.PropTypes.object

您甚至可以编写自己的高阶组件,自动将context 值作为&lt;Route&gt; components 的props 注入。这将是一个“穷人的商店”。您可以让它工作,但与使用上述商店库之一相比,效率很可能会降低,并且存在更多错误。

React.cloneElement 呢?

还有另一种方法可以为&lt;Route&gt;component 提供道具,但它一次只能工作一个级别。本质上,当 React Router 基于当前路由渲染组件时,它首先为嵌套最深的匹配 &lt;Route&gt; 创建一个元素。然后,在为下一个嵌套最深的&lt;Route&gt; 创建元素时,它会将该元素作为children 属性传递。这意味着在第二个组件的render 方法中,您可以使用React.cloneElement 克隆现有的children 元素并为其添加额外的道具。

const Bar = (props) => (
  <div>These are my props: JSON.stringify(props)</div>
)

const Foo = (props) => (
  <div>
    This is my child: 
      props.children && React.cloneElement(props.children,  username: props.username )
    
  </div>
)

这当然很乏味,尤其是如果您需要通过多个级别的&lt;Route&gt;components 传递此信息。您还需要在基础 &lt;Route&gt; 组件(即 &lt;Route path='/' component=Base&gt;)中管理您的状态,因为您无法从 &lt;Router&gt; 的父组件中注入状态。

【讨论】:

问题是改变路由本身的状态 请注意,React.PropTypes 自 React v15.5 以来已移至不同的包中。请改用“prop-types”库:npmjs.com/package/prop-types 那么为什么LocationDescriptorObject接口中有一个.state属性呢?这令人困惑 - 我在文档中找不到任何内容......我的意思是我无论如何都在使用 redux,但它的意图是什么? @YannicHamann location.state 来自浏览器(history.pushState/replaceState 的第一个参数是与位置关联的可序列化状态)并带有警告,例如当您直接导航到页面时它为 null .我发现 location.state 最适合临时数据,例如登录后导航到的 URL,而“真实”状态最好保留在其他地方。 这在 2022 年已经过时了。它也是固执己见和误导的。单独使用全局存储与通过导航传递状态具有相同的问题,并且作者没有表明状态需要“在整个应用程序中可访问”。他们只需要在导航时传递状态,这并不需要全局存储。【参考方案2】:

我知道回答这个问题可能会迟到,但是对于将来对此感到疑惑的任何人,您可以这样做:

export default class Routes extends Component 
constructor(props) 
    super(props);
    this.state =  config: 'http://localhost' ;

render() 
    return (
        <div>
            <BrowserRouter>
                <Switch>
                    <Route path="/" exact component=App />
                    <Route path="/lectures" exact render=() => <Lectures config=this.state.config /> />
                </Switch>
            </BrowserRouter>
        </div>
    );

通过这种方式,您可以访问 Lecture 组件中的配置道具。 这是围绕这个问题的一点点,但首先它是一个很好的接触! :)

【讨论】:

【参考方案3】:

一旦安装了路由器组件,您就无法更改 React-router 的状态。您可以编写自己的 html5 路由组件并监听 url 的变化。

class MyRouter extend React.component 
   constructor(props,context)
   super(props,context);
   this._registerHashEvent = this._registerHashEvent.bind(this)

  

 _registerHashEvent() 
    window.addEventListener("hashchange", this._hashHandler.bind(this), false);
  
  ...
  ...

render()
  return ( <div> <RouteComponent /> </div>)

【讨论】:

我认为这并不能真正回答原始问题。这如何提供将路线与状态联系起来的能力?【参考方案4】:

对于像我这样的人,

你也可以正常渲染这个:

import 
  BrowserRouter as Router, 
  Router, 
  Link, 
  Switch 
 from 'react-router-dom'

<Router>
  <Switch>
   <Link to='profile'>Profile</Link>

   <Route path='profile'>
     <Profile data=this.state.username />
   </Route>
   <Route component=PageNotFound />
  </Switch>
</Router>

这对我有用!

【讨论】:

【参考方案5】:

请注意,如果您使用的是query string,则需要添加search

例如:


  key: 'ac3df4', // not with HashHistory!
  pathname: '/somewhere',
  search: '?some=search-string',
  hash: '#howdy',
  state: 
    [userDefined]: true
  

花了大约 20 分钟才弄清楚为什么我的路线没有被渲染?

【讨论】:

以上是关于如何通过 React Router 传递状态?的主要内容,如果未能解决你的问题,请参考以下文章

将 props 传递给 React Router 子路由

如何使用 onClick 方法通过 react-router 传递道具

React + Redux + Router - 我应该为所有页面/组件使用一个状态/存储吗?

如何使用 react-router 通过 Route 渲染传递 redux 商店道具?

如何使用 ReactJS 通过我的 React-Router 传递数据?

在 React-Router 中将状态作为道具从父级传递给子级?