Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-router2 迁移到 react-router4 关注点 #6

Open
liyatang opened this issue May 17, 2017 · 15 comments
Open

react-router2 迁移到 react-router4 关注点 #6

liyatang opened this issue May 17, 2017 · 15 comments

Comments

@liyatang
Copy link
Contributor

liyatang commented May 17, 2017

以下mark MA切换react-router4过程中注意点

文档

https://github.com/react-translate-team/react-router-CN
https://reacttraining.cn/

使用了两个库
react-router 通用库,web 和 Native 都可用
react-router-dom web用

Router Route

不累赘,看文档和MA代码吧,✌️

Link

// 以前
import {Link} from 'react-router';

// now
import {Link} from 'react-router-dom';

history

service.js 中history的提供方式变了

// 以前
import {hashHistory} from 'react-router';
const history = hashHistory;

// now
import createHashHistory from 'history/createBrowserHistory'
const history = createHashHistory()

query search

query 切到了 search,且search是字符串类型
query => search(obj =>string)

具体文档见 https://github.com/ReactTraining/history

push && replace

// 以前
history.push({
    pathname: '/home',
    query: {
        the: 'query'
    }
})

// now
history.push({
    pathname: '/home',
    search: '?the=query',
})

history.replace 同理,不累赘

props

route提供的props也变更。react-router2 提供 location, history, params,react-router4 提供 location, history, match

其中
location.query (obj) => location.search(string) 需要自己对字符串处理,可引入 query-string

props.params -> props.match.params

兼容

由于 query 和 params 以前业务用的多,一下子切过去有风险,估做了兼容逻辑,保证react-router2的这部分能用,但未来还是会废弃掉的

怎么做的 ?

会对react-router提供的props做加工处理,提供query 和 params,代码见gm-util

import queryString from 'query-string';

export default function processReactRouterProps(props) {
    const newProps = Object.assign({}, props);
    newProps.location.query = queryString.parse(props.location.search);
    newProps.params = props.match.params || {}; // 不止 || 是否有意义
    return newProps;
}

对history的方法做处理

import _ from 'lodash';
import queryString from 'query-string';

function processHistory(history) {
    const _push = history.push;
    const _replace = history.replace;

    history.push = function (one) {
        if (!_.isPlainObject(one)) {
            return _push.apply(this, arguments);
        }

        const o = Object.assign({}, one);

        if (o.query) {
            o.search = queryString.stringify(o.query);
        }

        _push.apply(this, [o]);
    };

    history.replace = function (one) {
        if (!_.isPlainObject(one)) {
            return _replace.apply(this, arguments);
        }

        const o = Object.assign({}, one);

        if (o.query) {
            o.search = queryString.stringify(o.query);
        }

        _replace.apply(this, [o]);
    };

    return history;
}

export default processHistory;
@fondadam
Copy link
Contributor

添加一些没记录到的地方

1. Router/Route 的改变

// V2 or V3
import { Router, Route, hashHistory } from 'react-router';

<Router history={hashHistory}>
  <Route path='/foo' component={Foo} />
  <Route path='/bar' component={Bar} />
</Router>
// V4 Router组件里只能渲染一个组件
import {
    HashRouter as Router,
    Route
} from 'react-router-dom';

<Router>
  <div>
    <Route path='/foo' component={Foo} />
    <Route path='/bar' component={Bar} />
  </div>
</Router>

2. 组件嵌套

// V2 or V3 路由组件嵌套
import { Router, Route, hashHistory } from 'react-router';

<Router history={hashHistory}>
  <Route path='/' component={App}>
    <Route path='foo' component={Foo} />
    <Route path='bar' component={Bar} />
  </Route>
</Router>
// V4 Router 的路由组件嵌套
import {
    HashRouter as Router,
    Route,
    Switch
} from 'react-router-dom';

<Router>
 <Route path="/" component={(props) => (
    <App {...props}>
      <Switch>
        <Route path='/foo' component={Foo} />
        <Route path='/bar' component={Bar} />
      </Switch>
    </App>
  )}/>
</Router>

3. 路由的生命周期

react-router V4中去掉了on****的路由生命周期的钩子,但是你可以在组件中用componentDidMountcomponentWillMount 代替 onEnter,可以用componentWillUpdatecomponentWillReceiveProps代替 onUpdate,你可以用componentWillUnmount 代替 onLeave

@svkd9
Copy link

svkd9 commented Jun 22, 2017

push之后地址栏url改变但是页面没有刷新,请问你有没有遇到过
import createHashHistory from 'history/createBrowserHistory'
const history = createHashHistory()

history.push({ pathname: '/login' })

@liyatang
Copy link
Contributor Author

@svkd9 情况太多,建议提供简易版demo。
几个思路可以看下:
1 路由设置对了?
2 跳转过去的路由有错?

@svkd9
Copy link

svkd9 commented Jun 22, 2017

感谢回复,下面是我写的了一个简单的demo
https://github.com/svkd9/react-router_v4_demo.git

@liyatang
Copy link
Contributor Author

@svkd9 sorry,同蒙圈。 不过我换成 createHashHistory 就没问题了。 至于 createBrowserHistory 为啥不行目前还没答案。

@yongningfu
Copy link

yongningfu commented Aug 1, 2017

我来解答一下为何 createHashHistory可以, 但是createBrowserHistory 不行吧!
首先,上面的HashRouter 和BrowserRoute里面都是内含有 history对象的,这一点 @liyatang 也说了。

为何hashHistory可以,但是broserHistory不可以呢,

其实这个问题解释不难,照着逻辑去定位一下就可以了
(ps: router本身是用history事件订阅去触发router组件更新的, 所有关键是寻找如何触发history订阅的事件)

因为window对象有个 hashchange 事件 可以监听哈希的变化
在hashHistory中, 通过window去监听hashchange变化 而去触发history订阅的事件的(即router组件更新)源码 即无论哪个history对象触发的hash变化,都会被window监听到,而且执行路由更新,所以hashRouter是没问题的。

但是浏览器并没有直接监听url变化的事件, 所以broserHistory变化本身只能使用同一个history对象的执行本身的的事件订阅模式去触发组件的更新,当然代码里面还增加了 对window对象的的 hashchange 和 popstate 事件监听

相关源码tips1tips2

@liyatang @svkd9

@liyatang
Copy link
Contributor Author

liyatang commented Aug 1, 2017

豁然开朗,谢谢。 @yongningfu

@yongningfu
Copy link

大概浏览了一下你们组件库的代码,确实逻辑处理的还是非常清晰的,能直接封装一套适合公司用的ui, 感觉还是很棒的,你们还招人么? @liyatang

@liyatang
Copy link
Contributor Author

liyatang commented Aug 1, 2017

@yongningfu 加微信 wxliyatang

@Statfine
Copy link

@fondadam @liyatang 关于用componentDidMount 或 componentWillMount 代替 onEnter的问题, 如果路由的位置用到onEnter(nextState, replace, callback);因为该方法中的异步如果没有返回,页面就不会渲染。所以有时会将用户的验证放在该方法中的,可是如果改成放到componentWillMount,如果有异步验证并且还没返回的时候,是不是页面就不渲染组建(组件中的请求需要用户验证之后)同时页面还需要特意声明一个state状态

@ghost
Copy link

ghost commented Sep 28, 2017

是否我需要js跳转路由的组件,都需要
import createHistory from 'history/createHashHistory'
const history = createHistory()
这样不麻烦. 不能用this.出来么

@liyatang
Copy link
Contributor Author

@AlanZou007 你可以把history保存起来

@JesterCheung
Copy link

请问,router@4中,“只有当访问地址和路由匹配时,一个 React component 才会被渲染”。我现在是结合了antd,实现tab标签。而tab标签的切换是不会导致地址栏发生变化的,因此不会渲染组件。有没有解决办法呢?

@liyatang
Copy link
Contributor Author

@JesterCheung 理下你需求是否这样: tab切换会带动地址栏变化?

那你在切换的时候 history.push(xxxx) 就ok了,把tab的状态当成路由的参数。

eg 路由设置成 xxxxxx/#/A/B?tab=1

切换tab的时候

history.push({
    pathname: 'xxxxxx/', 
    search: '?tab=1'
})

btw,tab一般不建议做成导致地址栏变化。 至于切换tab,组件是否渲染是tab的实现方式问题,属于另外个话题。 可以把代码抛出来讨论。

@j710328466
Copy link

j710328466 commented Dec 18, 2017

import React, { Component } from 'react';
import { Router, Route, Link, Switch, HashHistory } from 'react-router-dom';
import PropTypes from 'prop-types';
import './app.css'
import Goods from './pages/goods/goods'
import Active from './pages/active/active'
import Detail from './pages/detail/detail'
import Home from './pages/home/home'
import createHistory from 'history/createHashHistory'
const history = createHistory()


class App extends Component {
  constructor () {
    super()
    this.state = {
    }
  }
  
  render() {
    return (
      <Router history={history}>
        <div className="container"> 
          <div className="siderBar">
            <ul>
              <li className="siderBar_btn"><Link to="/">首页</Link></li>
              <li className="siderBar_btn"><Link to="/goods">商品专区</Link></li>
              <li className="siderBar_btn"><Link to="/detail">商品详情</Link></li>
              <li className="siderBar_btn"><Link to="/active">活动</Link></li>
            </ul>
          </div>
          
          <div className="content">
            <h1 className="title">解码器1.0</h1> 
            <Route render={({ location }) => {
              return (
                <Switch>
                  <Route path="/" component={Home} />
                  <Route path="/goods" component={Goods} />
                  <Route location={location} path="detail" component={Detail} />
                  <Route location={location} path="active" component={Active} />
                </Switch>
              )
            }} />
            <div className="footer"><img src={require('./assets/logo.png')} /></div>
          </div>
        </div>
      </Router>
    )
  }
}

export default App;

请问我的问题出在哪?url变化,路由不跳转,重复点击的话提示Warning: Hash history cannot PUSH the same path; a new entry will not be added to the history stack
warning @ browser.js?e722:49
折腾了我好久,求告知谢谢~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants