检测查询参数react-router-dom v4.x中的更改并重新呈现组件 [英] Detect change in query param react-router-dom v4.x and re-render component

查看:91
本文介绍了检测查询参数react-router-dom v4.x中的更改并重新呈现组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不确定为什么在我进行查询参数更改后它会显示默认路由。这种问题有更好的方法吗?也许我不应该使用查询参数?开放接受教育!



版本
反应:^ 16.2.0,
react-dom:^ 16.2。 0,
react-router:^ 4.2.0,
react-router-dom:^ 4.2.2,






测试用例
https://codepen.io/adamchenwei/pen/YeJBxY?editors=0011



重现
的步骤点击首页 - >步骤1



预期行为
转到步骤1以及步骤2呈现正确的dom



实际行为
空,页面上没有任何呈现

  //对于此演示,我们使用UMD构建的反应 - router-dom 
const {
BrowserRouter,
Switch,
Route,
Link
} = ReactRouterDOM

const {
组件,
} =反应;

const Router = BrowserRouter;

const Step1View =()=> (
< div>
< h1>步骤1< / h1>
< / div>


const Step2View =() => (
< div>
< h1>步骤2< / h1>
< / div>


class Home extends Component {
构造函数(道具){
super(道具);
console.log(' - !!!')
this.state = {
step:1,
}
this.next = this.next.bind (这个);
}

next(stepNumber = 1){
this.props.history.push({
pathname:`/ adamchenwei / pen / YeJBxY?editors = 0011 /?step = $ {stepNumber}`,
});
const query = this.props.history.location.pathname;
console.log('--- aaaaa');
console.log(查询);
if(query ==='/ adamchenwei / pen / YeJBxY?editors = 0011 /?step = 1'){
this.setState({
step:1,
})
} else if(query ==='/ adamchenwei / pen / YeJBxY?editors = 0011 /?step = 2'){
this.setState({
step:2,
})
}
}
render(){
console.log('render !!!');
console.log(this);
const {
step
} = this.state;
console.log('--- step');
console.log(步骤);
返回(
< div>
< h1>欢迎使用龙卷风网站!< / h1>
< button onClick = {()=>这个。 next(1)}>第1步< / button>
< button onClick = {()=> this.next(2)}>第2步< / button>
{
step === 1?< h1>这里的第1步< / h1>:null
}
{
step === 2?< h1>这里的第2步< / h1>:null
}
< / div>

);
}
}

//主要组件呈现三个中的一个提供
//路由(假设一个匹配)。 / roster
//和/ schedule路由都将匹配任何使用/ roster或/ schedule启动
//的路径名。当路径名正好是字符串/时,/ route只匹配
//
const Main =()=> (
< main>
< Route exact path ='/'component = {Home} />
< / main>


// Header创建的链接可用于在路由之间导航
//。
const Header =()=> (
< header>
< nav>
< ul>
< li>< Link to ='/'> Home< / Link>< / li>
< li>< Link to ='/ roster'> Roster< / Link>< / li>
< li>< Link to ='/ schedule'> ; Schedule< / Link>< / li>
< / ul>
< / nav>
< / header>


const App =()=> (
< div>
< Header />
< Main />
< / div>


//这个演示使用HashRouter而不是BrowserRouter
//因为没有匹配URL的服务器
ReactDOM.render((
< Router>
< App / >
< / Router>
),document.getElementById('root'))


解决方案

从v4开始,React路由器不再在其位置对象中为您提供查询参数。原因是



有许多流行的包可以查询字符串解析/字符串化略有不同,每个对于某些用户,这些差异可能是正确方式,对于其他用户,这些差异可能是不正确。如果 React Router 选择了right,那么它只适合某些人。然后,它需要为其他用户添加一种替代其首选查询解析包的方法。 React Router没有内部使用搜索字符串来要求它解析键值对,所以它不需要选择其中哪一个应该是正确的。



包含它之后,只需在视图组件中解析期望查询对象的 location.search 就更有意义了。 p>

您可以通过覆盖来自react-router的withRouter来实现这一点,例如



customWithRouter.js

 从'recompose'导入{compose,withPropsOnChange};来自'react-router'的
import {withRouter};
从'query-string'导入queryString;

const propsWithQuery = withPropsOnChange(
['location','match'],
({location,match})=> {
return {
位置:{
...位置,
查询:queryString.parse(location.search)
},
匹配
};
}
);

导出默认撰写(withRouter,propsWithQuery)

以及需要查询的地方字符串,您只需使用它就像

 从'path / to / customWithRouter.js'导入withRouter; 

class Home extends Component {
constructor(props){
super(props);
console.log(' - !!!')
this.state = {
step:1,
}
this.next = this.next.bind (这个);
}

next(stepNumber = 1){
this.props.history.push({
pathname:`/ adamchenwei / pen / YeJBxY?editors = 0011& ; step = $ {stepNumber}`,
});
}
componentDidUpdate(prevProps){//使用componentDidUpdate,因为componentWillReceiveProps将从v16.3.0重命名为UNSAFE_componentWillReceiveProps,后来被删除
const {query:{step}} = this.props.history 。地点;
if(!_。isEqual(this.props.history.location.query,prevProps.history.location.query)){
this.setState({
step
} )
}
}
render(){
console.log('render !!!');
console.log(this);
const {
step
} = this.state;
console.log('--- step');
console.log(步骤);
返回(
< div>
< h1>欢迎使用龙卷风网站!< / h1>
< button onClick = {()=>这个。 next(1)}>第1步< / button>
< button onClick = {()=> this.next(2)}>第2步< / button>
{
step === 1?< h1>这里的第1步< / h1>:null
}
{
step === 2?< h1>这里的第2步< / h1>:null
}
< / div>

);
}
}

const HomeWithQuery = withRouter(Home);


I am not really sure why its showing the default route once I did a query param change. Is there a better approach for this kind of issue? maybe I shouldn't be using query param? Open to get educated!

Version "react": "^16.2.0", "react-dom": "^16.2.0", "react-router": "^4.2.0", "react-router-dom": "^4.2.2",


Test Case https://codepen.io/adamchenwei/pen/YeJBxY?editors=0011

Steps to reproduce Click on Home -> Step 1

Expected Behavior Go to Step 1 as well as Step 2 render correct dom

Actual Behavior Empty, nothing renders on the page

// For this demo, we are using the UMD build of react-router-dom
const {
  BrowserRouter,
  Switch,
  Route,
  Link
} = ReactRouterDOM

const {
  Component,
} = React;

const Router = BrowserRouter;

const Step1View = () => (
  <div>
    <h1> Step 1</h1>
  </div>
)

const Step2View = () => (
  <div>
    <h1> Step 2</h1>
  </div>
)

class Home extends Component {
  constructor(props) {
    super(props);
    console.log('-!!!')
    this.state = {
      step: 1,
    }
    this.next = this.next.bind(this);
  }

  next(stepNumber=1) {
    this.props.history.push({
      pathname: `/adamchenwei/pen/YeJBxY?editors=0011/?step=${stepNumber}`,
    });
    const query = this.props.history.location.pathname;
    console.log('---aaaaa');
    console.log(query);
    if (query === '/adamchenwei/pen/YeJBxY?editors=0011/?step=1') {
      this.setState({
        step: 1,
      })
    } else if (query === '/adamchenwei/pen/YeJBxY?editors=0011/?step=2') {
      this.setState({
        step: 2,
      })
    }
  }
  render() {
    console.log('render!!!');
    console.log(this);
    const {
      step
    } = this.state;
    console.log('---step');
    console.log(step);
    return(
      <div>
        <h1>Welcome to the Tornadoes Website!</h1>
        <button onClick={()=> this.next(1)} > Step 1</button>
        <button onClick={()=> this.next(2)} > Step 2</button>
        {
          step === 1 ? <h1>Step 1 here</h1> : null
        }
        {
          step === 2 ? <h1>Step 2 here</h1> : null
        }
      </div>

    );
  }
}

// The Main component renders one of the three provided
// Routes (provided that one matches). Both the /roster
// and /schedule routes will match any pathname that starts
// with /roster or /schedule. The / route will only match
// when the pathname is exactly the string "/"
const Main = () => (
  <main>
    <Route exact path='/' component={Home}/>
  </main>
)

// The Header creates links that can be used to navigate
// between routes.
const Header = () => (
  <header>
    <nav>
      <ul>
        <li><Link to='/'>Home</Link></li>
        <li><Link to='/roster'>Roster</Link></li>
        <li><Link to='/schedule'>Schedule</Link></li>
      </ul>
    </nav>
  </header>
)

const App = () => (
  <div>
    <Header />
    <Main />
  </div>
)

// This demo uses a HashRouter instead of BrowserRouter
// because there is no server to match URLs
ReactDOM.render((
  <Router>
    <App />
  </Router>
), document.getElementById('root'))

解决方案

React router from v4 onwards no longer gives you the query params in its location object. The reason being

There are a number of popular packages that do query string parsing/stringifying slightly differently, and each of these differences might be the "correct" way for some users and "incorrect" for others. If React Router picked the "right" one, it would only be right for some people. Then, it would need to add a way for other users to substitute in their preferred query parsing package. There is no internal use of the search string by React Router that requires it to parse the key-value pairs, so it doesn't have a need to pick which one of these should be "right".

Having included that, It would just make more sense to just parse location.search in your view components that are expecting a query object.

You can do this generically by overriding the withRouter from react-router like

customWithRouter.js

import { compose, withPropsOnChange } from 'recompose';
import { withRouter } from 'react-router';
import queryString from 'query-string';

const propsWithQuery = withPropsOnChange(
    ['location', 'match'],
    ({ location, match }) => {
        return {
            location: {
                ...location,
                query: queryString.parse(location.search)
            },
            match
        };
    }
);

export default compose(withRouter, propsWithQuery)

and wherever you need query string, you could simply use it like

import withRouter from 'path/to/customWithRouter.js';

class Home extends Component {
  constructor(props) {
    super(props);
    console.log('-!!!')
    this.state = {
      step: 1,
    }
    this.next = this.next.bind(this);
  }

  next(stepNumber=1) {
    this.props.history.push({
      pathname: `/adamchenwei/pen/YeJBxY?editors=0011&step=${stepNumber}`,
    });
  }
  componentDidUpdate(prevProps) {    // using componentDidUpdate because componentWillReceiveProps will be renamed to UNSAFE_componentWillReceiveProps from v16.3.0 and later removed
    const {query: { step } } = this.props.history.location;
    if(!_.isEqual(this.props.history.location.query, prevProps.history.location.query)) {
         this.setState({
             step
          })
    }
  }
  render() {
    console.log('render!!!');
    console.log(this);
    const {
      step
    } = this.state;
    console.log('---step');
    console.log(step);
    return(
      <div>
        <h1>Welcome to the Tornadoes Website!</h1>
        <button onClick={()=> this.next(1)} > Step 1</button>
        <button onClick={()=> this.next(2)} > Step 2</button>
        {
          step === 1 ? <h1>Step 1 here</h1> : null
        }
        {
          step === 2 ? <h1>Step 2 here</h1> : null
        }
      </div>

    );
  }
}

const HomeWithQuery = withRouter(Home);

这篇关于检测查询参数react-router-dom v4.x中的更改并重新呈现组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆