检测查询参数 react-router-dom v4.x 的变化并重新渲染组件 [英] Detect change in query param react-router-dom v4.x and re-render component

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

问题描述

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

版本反应":^ 16.2.0","react-dom": "^16.2.0",反应路由器":^ 4.2.0","react-router-dom": "^4.2.2",

<小时>

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

重现步骤点击主页 -> 步骤 1

预期行为转到第 1 步和第 2 步渲染正确的 dom

实际行为空,页面上没有任何内容

//对于这个演示,我们使用 react-router-dom 的 UMD 构建常量{浏览器路由器,转变,路线,关联} = ReactRouterDOM常量{零件,} = 反应;const 路由器 = BrowserRouter;const Step1View = () =>(<div><h1>步骤1/h1

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

)类 Home 扩展组件 {构造函数(道具){超级(道具);console.log('-!!!')this.state = {步骤1,}this.next = this.next.bind(this);}下一步(步数= 1){this.props.history.push({路径名:`/adamchenwei/pen/YeJBxY?editors=0011/?step=${stepNumber}`,});const 查询 = this.props.history.location.pathname;console.log('---aaaaa');控制台日志(查询);if (query === '/adamchenwei/pen/YeJBxY?editors=0011/?step=1') {this.setState({步骤1,})} else if (query === '/adamchenwei/pen/YeJBxY?editors=0011/?step=2') {this.setState({第2步,})}}使成为() {console.log('渲染!!!');控制台日志(这个);常量{步} = this.state;console.log('---步骤');控制台日志(步骤);返回(<div><h1>欢迎来到龙卷风网站!</h1><button onClick={()=>this.next(1)} >步骤 1<button onClick={()=>this.next(2)} >步骤 2{步骤 === 1 ?<h1>此处为步骤 1</h1>: 空值}{步骤 === 2 ?<h1>这里是步骤2</h1>: 空值}

);}}//Main 组件呈现提供的三个组件之一//路由(假设匹配).两个/roster//和/schedule 路由将匹配任何以开头的路径名//使用/roster 或/schedule./路线只会匹配//当路径名恰好是字符串/"时const Main = () =>(<主要><路由精确路径='/' component={Home}/></main>)//Header 创建可用于导航的链接//路由之间.const 标题 = () =>(<标题><导航><ul><li><Link to='/'>主页</Link></li><li><Link to='/roster'>Roster</Link></li><li><Link to='/schedule'>Schedule</Link></li></nav></标题>)const App = () =>(<div><标题/><主要/>

)//此演示使用 HashRouter 而不是 BrowserRouter//因为没有服务器来匹配 URLReactDOM.render((<路由器><应用程序/></路由器>), document.getElementById('root'))

解决方案

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

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

有了它,在需要查询对象的视图组件中解析 location.search 会更有意义.

您可以通过从 react-router 覆盖 withRouter 来实现这一点

customWithRouter.js

import { compose, withPropsOnChange } from 'recompose';从反应路由器"导入 { withRouter };从查询字符串"导入查询字符串;const propsWithQuery = withPropsOnChange(['位置','匹配'],({位置,匹配}) =>{返回 {地点: {...地点,查询:queryString.parse(location.search)},比赛};});导出默认撰写(withRouter,propsWithQuery)

在任何需要查询字符串的地方,您都可以简单地使用它

 import withRouter from 'path/to/customWithRouter.js';类 Home 扩展组件 {构造函数(道具){超级(道具);console.log('-!!!')this.state = {步骤1,}this.next = this.next.bind(this);}下一步(步数= 1){this.props.history.push({路径名:`/adamchenwei/pen/YeJBxY?editors=0011&step=${stepNumber}`,});}componentDidUpdate(prevProps) {//使用 componentDidUpdate 因为 componentWillReceiveProps 将从 v16.3.0 重命名为 UNSAFE_componentWillReceiveProps 并且后来被移除const {query: { step } } = this.props.history.location;if(!_.isEqual(this.props.history.location.query, prevProps.history.location.query)) {this.setState({步})}}使成为() {console.log('渲染!!!');控制台日志(这个);常量{步} = this.state;console.log('---步骤');控制台日志(步骤);返回(<div><h1>欢迎来到龙卷风网站!</h1><button onClick={()=>this.next(1)} >步骤 1<button onClick={()=>this.next(2)} >步骤 2{步骤 === 1 ?<h1>此处为步骤 1</h1>: 空值}{步骤 === 2 ?<h1>这里是步骤2</h1>: 空值}

);}}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天全站免登陆