检测查询参数 react-router-dom v4.x 的变化并重新渲染组件

IT技术 javascript reactjs react-router react-router-v4 query-parameters
2021-03-13 09:18:44

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

版本 "react": "^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

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

// 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'))
2个回答

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

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

包括在内,location.search在需要查询对象的视图组件中进行解析会更有意义

您可以通过从 react-router 覆盖 withRouter 来一般地做到这一点

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)

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

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);
同意,上面的答案是针对您的场景的类似解决方案。也许采用上面的答案并考虑我在下面陈述的替代方法,我们都可以从中学习:-) 希望您找到了解决方案。
2021-04-29 09:18:44
非常有趣的解决方案!
2021-05-11 09:18:44
@Francis Leigh 替代方案完美无缺,但您的选择非常坚持问题本身......我想我会将最佳解决方案留给其他用户最多的投票!感觉你的两个回答都很好。
2021-05-14 09:18:44

// A lose explanation. URL = localhost:3000/myRoute/2

<Router history={history}>
  <Route path="/myRoute/:id" component={App} />
</Router>

class App extends Component {
  render() {
    const { id } = this.props.match.params
    
    return (
      <div>
        {id === 2 ? <div>id is 2</div> : <div>id not 2</div>}
      </div>
    )
  }
}

爱它!是的,match.params 似乎是最简单的方法,而不是与查询参数搏斗,在这个简单的情况下,这非常有效
2021-04-23 09:18:44
这如何检测查询参数的变化?
2021-05-01 09:18:44