环境
react:16.13.1
react-router:5.2.0参考文章
history.listen
场景 1:在查问页面,批改查问表单后,刷新数据.而后跳转页面再返回,须要把之前的表单数据还原
场景 2:在其余的业务页面,点击带有参数的链接,在跳转后须要将带过去的参数设置的到表单中
计划 1(放弃)
应用动静路由,例如:/list/:type/:cat/:page
.这种形式不适宜,因为参数的数量不确定,而且查问时容许不带参数
计划 2
应用search
参数.该计划能够解决方案 1 的问题
实现
- 应用类装璜器.
- 容许应用默认值
- 主动监听
search
变动 - 将
search
转化为对象,方便使用 - 提供
search
字符串和{key:value}
对象的互转工具
代码
计划中没有应用history.listen
,因为无奈获取旧的location
,从而判断路由中的search
是否变动(可能是其余的变动)
src/utils/search-listener.js
import { withRouter } from 'react-router-dom'/** * @desc 将Location中的search字符串转换为对象 * @param {string} search */export function getSearchObject(search = '') { const result = {} if (search) { const searchStr = search.split('?')[1] const searchKeyValueArr = searchStr.split('&') for (let i = 0; i < searchKeyValueArr.length; i++) { const [key, value] = searchKeyValueArr[i].split('=') result[key] = decodeURIComponent(value) } } return result}/** * @desc 将对象转化为search字符串 * @param {object} obj */export function objectToSearch(obj = {}) { let searchStr = '' for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = encodeURIComponent(obj[key]) searchStr += `${key}=${value}&` } } return searchStr ? '?' + searchStr.slice(0, -1) : ''}/** * @desc 可监听search变动的装璜器 * * @desc 限度: * @desc state.search用于寄存参数对象,不能命名抵触 * @desc onRouteSearchUpdate用于监听更新,不能命名抵触 * * @param {boolean?} listenOnDidMount 是否在组件初始化时就触发'onRouteSearchUpdate' */export default function withSearchListener(listenOnDidMount = true) { let initSearch = {} return WrappedComponent => withRouter( class extends WrappedComponent { componentDidMount() { // 初始化默认的search initSearch = this.state.search || {} if (typeof WrappedComponent.prototype.componentDidMount === 'function') { WrappedComponent.prototype.componentDidMount.call(this) } if (listenOnDidMount) { this.onRouteSearchUpdate(getSearchObject(this.props.location.search), this.props.location) } } componentDidUpdate(prevProps) { if (typeof WrappedComponent.prototype.componentDidUpdate === 'function') { WrappedComponent.prototype.componentDidUpdate.call(this) } if (prevProps.location.search !== this.props.location.search) { this.onRouteSearchUpdate(getSearchObject(this.props.location.search), this.props.location) } } /** * @desc 当路由中的'search'更新时触发 * @param {string?} search * @param {object?} location */ onRouteSearchUpdate(search = {}, location = {}) { // 依据默认的search来合并出新的search const nextSearch = { ...initSearch, ...search } this.setState({ search: nextSearch }, () => { if (typeof WrappedComponent.prototype.onRouteSearchUpdate === 'function') { WrappedComponent.prototype.onRouteSearchUpdate.call(this, nextSearch, location) } }) } } )}
应用
import React, { Component } from 'react'import withSearchListener from '@/utils/search-listener'@withSearchListener()class Page extends Component { state = { search: { a: '1', b: '1' } } onRouteSearchUpdate(search = {}, location = {}) { console.log('search updated', search, location) } render() { return ( <div> <h1>Search route</h1> <h2>search :{JSON.stringify(this.state.search)}</h2> </div> ) }}
残缺的例子
/** * @name SearchRoute * @author darcrand * @desc */import React, { Component } from 'react'import { Link } from 'react-router-dom'import withSearchListener, { objectToSearch } from '@/utils/search-listener'async function apiGetData(params = {}) { console.log('apiGetData', params) return Array(10) .fill(0) .map(_ => ({ id: Math.random(), title: `title - ${~~(Math.random() * 100)}` }))}const optionsA = Array(5) .fill(0) .map((_, i) => ({ value: String(i + 1), label: `A-${i + 1}` }))const optionsB = Array(5) .fill(0) .map((_, i) => ({ value: String(i + 1), label: `B-${i + 1}` }))@withSearchListener()class SearchRoute extends Component { state = { search: { a: '1', b: '1' }, list: [] } onParamsChange = (field = {}) => { const { search } = this.state this.props.history.replace('/search-route' + objectToSearch({ ...search, ...field })) } onRouteSearchUpdate(search = {}, location = {}) { console.log('onRouteSearchUpdate', search, location) this.getData() } getData = async () => { const res = await apiGetData(this.state.search) this.setState({ list: res }) } render() { return ( <> <h1>领有 路由监听性能的组件</h1> <p>通过链接跳转</p> <ul> <li> <Link replace to='/search-route'> 空参数 </Link> </li> <li> <Link replace to='/search-route?a=3'> 参数 a </Link> </li> <li> <Link replace to='/search-route?b=2'> 参数 b </Link> </li> <li> <Link replace to='/search-route?a=4&b=3'> 参数 ab </Link> </li> </ul> <p>通过表单参数模仿跳转</p> <section> {optionsA.map(v => ( <button key={v.value}> <label> <input type='radio' name='a' value={v.value} checked={this.state.search.a === v.value} onChange={e => this.onParamsChange({ a: e.target.value })} /> <span>{v.label}</span> </label> </button> ))} </section> <section> {optionsB.map(v => ( <button key={v.value}> <label> <input type='radio' name='b' value={v.value} checked={this.state.search.b === v.value} onChange={e => this.onParamsChange({ b: e.target.value })} /> <span>{v.label}</span> </label> </button> ))} </section> <h2>search :{JSON.stringify(this.state.search)}</h2> <ol> {this.state.list.map(v => ( <li key={v.id}>{v.title}</li> ))} </ol> </> ) }}export default SearchRoute