【问题标题】:How do I pass state through React_router?如何通过 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
  },

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

【问题讨论】:

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

标签: reactjs react-router browser-history


【解决方案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 分钟才弄清楚为什么我的路线没有被渲染?

      【讨论】:

        猜你喜欢
        • 2023-02-13
        • 1970-01-01
        • 2017-05-19
        • 2020-08-29
        • 2018-02-28
        • 1970-01-01
        • 1970-01-01
        • 2019-10-19
        • 2018-01-29
        相关资源
        最近更新 更多