环境
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
发表回复