一、我以为的children
children嘛,child的复数形式,所以顾名思义,某个组件的children(this.props.children/props.children)其实就是这个组件的子组件,表现在代码中,无非就是(如下简易演示demo)
<Parent>
<Child1></Child1>
<Child2></Child2>
...
<Childn></Childn>
</Parent>
在parent组件中获取到的children就是 {Child1,Child2,....Childn}
二、项目需求
实际开发中遇到了一个挺合理的需求,但是前端真的是束手无策,描述如下:
某个页面PageA,在PageA触发跳转到PageB页面,要求如下:
1、pageA——>PageB——>pageA:pageA页面的查询条件需要保存(如果有的话),即就是在pageA以某个条件进行筛选查询后进入PageB再进入pageA,此时pageA应该保存在跳转之前的状态(数据、查询条件);
2、pageA进入其他页面(即就是通过切换菜单栏切换页面)后再回来不需要保存之前的状态(eg:pageA进入pageC再回到pageA,此时pageA应该恢复初始默认时的状态,即不保存查询条件)
现状:
项目用的是ant中的pro4脚手架搭建(https://pro.ant.design/index-cn),内部数据使用dva的Redux进行管理,所以所有的查询条件都会被保存到redux中(用户不强制刷新页面的话),导致,无论何种切换(跳)页面,数据(查询条件始终被保存)
解决思路:
在组件销毁的生命周期(componentWillUnmount)中,清空该页面的所有存在redux中的数据(一般是在项目model层,写一个方法)
遗留问题:
解决了页面跳转不保存数据(查询条件)的问题,但是用户在某个页面内点击进入到另一个页面再回来的场景(需要保存数据)
解决思路2:
在组件销毁的生命周期(componentWillUnmount)中,分情况清空该页面的所有存在redux中的数据,本次需求即就是在从页面中点击后跳转再回去
if(从页面中点击跳转再回去){
保存之前数据
}else{
不保存数据即就是清空数据
}
遇到问题:
怎么判断用户是从页面中点击后跳转的呢?我想到了监听回退事件,再回退事件中重新对redux中的变量赋值,but,这个听起来就很复杂,如何 监听?重新对redux中的变量赋值,这听起来就很复杂
同事思路:
同事说他们之前遇到过这种需求,是通过将组建绑定到一个“入口文件”中来实现的,具体思路:
A、将需要保存数据的相关页面(组件)集中到一个入口文件;
B、在入口组件销毁的生命周期方法中清空数据
一点小小解释:
可以简单理解为:需要保留数据的页面(组件)之间有一个共同的父组件,在父组件销毁的生命周期方法中清空数据(需要保留数据的页面切换时,不会调用父组件销毁的生命周期方法,只有在非相关页面(没有数据关联的页面)页面切换时才会调用销毁方法,清空数据)
分析继续:
感觉思路没啥问题,问题就在于
1、平白无故多出一个组件,还是两个页面的父组件,这个父组件路由怎么配置,这难道不仅仅是多出一个组合两个页面在一起的页面?
2、同事之前的代码是pro1,版本太老旧了,而且还用到一些工具类,看不懂
思路受阻,前辈代码又看不懂……但是有一点是肯定的,他们之前确实用这种方法实现了这种需求,虽然看不懂实现的细节,但是还是可以看看代码结构什么的,于是……
父组件(入口文件)代码结构如下:
import React, { PureComponent, Fragment } from 'react';
import { connect } from 'dva';
import { Route, Redirect, Switch } from 'dva/router';
import NotFound from '../../Exception/404';
import { getRoutes } from '../../../utils/utils';
@connect()
export default class XXXWrap extends PureComponent {
componentWillUnmount() {
this.props.dispatch({
type: 'xx/save',
payload: {
xxx: {},
},
});
}
render() {
const { match, routerData } = this.props;
return (
<Fragment>
<Switch>
{getRoutes(match.path, routerData).map(item => (
<Route key={item.key} path={item.path} component={item.component} exact={item.exact} />
))}
<Redirect exact from="/xx/service" to="/xx/service/yy" />
<Route render={NotFound} />
</Switch>
</Fragment>
);
}
}
突然有了一点发现:
这个所谓的入口组件,内容都是Route哎,也就是说入口组件通过Router将其他组件“包”在一起的
这是有疑问:
这个入口组件的路由怎么配?pro1的配置方法太麻烦了,不想看……
同事也在研究,发现Pro2版本同样的地方(List.js)中放弃了pro1版本的写法,而是直接使用children
路由怎么配,看代码:在路由文件中,搜索/List/List,还真被我找到了
原来它是把入口文件在路由配置文件中,作为component
运行代码找到对应页面:
研究了一下发现:
这根本不是我想要的效果呀,虽然它还是多出了一个页面来将子页面整合到一起……
不对!在List中没有啥问题,但是其他页面也没有将List作为父组件,怎么会是List的children?难道,children不仅仅是代码结构上嵌套表现出来的子组件,路由配置中只要某个组件中routes的组件,都是这个组件的children!好像讲得通!
在这种思路的引导下,我整理出新的代码,如下:
import React, { Component, Fragment } from 'react';
import { connect } from 'dva';
import { Dispatch } from 'redux';
interface IXXIndexProps {
dispatch: Dispatch;
}
@connect()
class XXIndex extends Component<IXXIndexProps > {
componentWillUnmount(): void {
/*
*
销毁redux中数据的逻辑
*/
}
render() {
const { children } = this.props;
return <Fragment>{children}</Fragment>;
}
}
export default XXIndex ;
在路由文件中配置如下:
{
path: '/xx/yy',
name: 'll',
hideChildrenInMenu: true,
component: './xx/XXIndex.tsx',
routes: [
{
path: '/xx/yy',
name: 'll',
component: './xx/XXAnalysis',
},
{
path: '/xx/pageA',
component: './xx/pageA',
},
{
path: '/xx/pageB',
component: './xx/pageB',
},
],
},
然后
问题成功的解决了
感觉废话有点多,但这确实是发现并解决这个问题坎坷的血泪史,真的是柳暗花明又一村呀!
--------------------------------干货整理----------------------------------------------------
需求:
点击菜单栏跳转,不需要保留页面查询条件;页面内部跳转再回去,需要保留页面查询条件
解决思路:
①将需要保留查询条件的相关页面使用一个入口文件“包裹”;②、在路由文件中,将入口文件设置为routes(包含了相关页面路由)同级别的component
你可能不知道的children:
router配置文件中,在routes中路由对应的文件,自动作为组件的children
eg:
{
path: '/a/b',
name: 'xx',
hideChildrenInMenu: true,
component: './a/b/ParenntIndex',
routes: [
{
path: '/l/cc',
name: 'l-cc',
component: './l/PageA',
},
{
path: '/l/cc/dd',
component: './l/cc/PageB',
},
{
path: '/l/cc/ff',
component: './l/PageC',
},
],
},
在ParenntIndex获取children,则就是{PageA,PageB,PageC}