Open soulcm opened 7 years ago
此处所说的组件,是指modal、tooltip、toast、message等等这类公共组件
因为最近在对项目进行一些优化,tooltip组件需要进行重构,因此在写的过错中发现前面的思路感觉根本不对了,在此记录一下
以前的tooltip长这样
class Tooltip extends React.component { //省略部分代码,直接看render render() { const {placement} = this.props; return( <div className="tooltip tooltip-right"> {this.props.children} </div> ) } } //调用的地方 import Tooltip from 'somepath/Tooltip'; class BusinessComp extends React.component { constructor(props) { super(props); this.state = { showTip: false } this.handleShowTip = this.handleShowTip.bind(this); } handleShowTip() { //计算tip位置的代码,并放入tipData中 this.setState({ showTip: !showTip tipData: tipData }) } tipRender() { if (this.state.showTip) { return ( <Tooltip style={style}>我是tip</Tooltip> ) } } render() { return ( <div> <span onClick={this.handleShowTip}></span> {this.tipRender()} </div> ) } }
这样造成每次页面都要去计算tip位置,并通过props传入,而且render出来的tip也是属于当前页面元素内部,有可能嵌套过多时样式会出现问题,后来通过观察ant-design的组件样式以及源码发现,一般都是把这类组件渲染到body里面,因此开始了重构之路。
首先不应该每次都要手动计算位置然后传入,应该是组件自己操作,而且应该想办法渲染到body上面,因此改造成下面这样
//引入方式改变 import Tooltip from 'somepath/Tooltip'; class BusinessComp extends React.component { render() { return ( <div> <Tooltip content="我是tip" placement="left"><span>点击我弹出tip</span></Tooltip> </div> ) } } //Tooltip class Tooltip extends React.component { constructor(props) { super(props); this.state = { visible: !!props.visible } this.handleClick = this.handleClick.bind(this); } componentDidMount() { this.getContainer();//获取到需要插入进body的元素 } getContainer() { this.getInstancePosition(); //计算tip的位置 this.container = document.createElement('div'); const parent = this.props.parentSelector; parent.appendChild(this.container); //插入container this.renderTip(this.props); //渲染tip } renderTip(props) { const {placement, title, content, style} = props; ReactDOM.unstable_renderSubtreeIntoContainer(this, <div className={`tooltip tooltip-${placement}`} style={assign({width: 300, opacity: 0, display: 'none'}, style)} ref={(c) => this.tipDom = c}> <div className="title">{title}</div> <div dangerouslySetInnerHTML={{__html: content}}></div> </div>, this.container ) } handleClick() { this.setState({ visible: !this.state.visible }) } render() { const {children, placement, style} = this.props; const child = React.isValidElement(children) ? children : <span>{children}</span>; //判断是否为react element const newChildProps = {}; //绑定方法或添加其他属性, 如onClick/onMouseEnter等等 newChildProps.onClick = this.handleClick; return React.cloneElement(child, newChildProps) //render中只需要渲染child } }
上述只是部分代码和实现思路
写这个时候,开始是直接创建的div,但发现那样传进来的props属性没法加到弹出的div上面去,后来一直翻阅蚂蚁的源码,会发现都是在react-componet各组件上面进行的封装,而react-component里面用到了ReactDOM.unstable_renderSubtreeIntoContainer这个方法,但react官网API里面并没有把这个给列出来,估计是一个不太正式的API,但还是希望react把这个API放出来,因为很多情况下确实需要用到。
PS. 听说facebook正在研究react专门针对dom的处理方式,还是期待下的
总结:
toast.info('我是toast')
React v16 ReactDOM.createPortal(child, container)
react部分组件写法的思考
此处所说的组件,是指modal、tooltip、toast、message等等这类公共组件
因为最近在对项目进行一些优化,tooltip组件需要进行重构,因此在写的过错中发现前面的思路感觉根本不对了,在此记录一下
以前的tooltip长这样
这样造成每次页面都要去计算tip位置,并通过props传入,而且render出来的tip也是属于当前页面元素内部,有可能嵌套过多时样式会出现问题,后来通过观察ant-design的组件样式以及源码发现,一般都是把这类组件渲染到body里面,因此开始了重构之路。
首先不应该每次都要手动计算位置然后传入,应该是组件自己操作,而且应该想办法渲染到body上面,因此改造成下面这样
上述只是部分代码和实现思路
写这个时候,开始是直接创建的div,但发现那样传进来的props属性没法加到弹出的div上面去,后来一直翻阅蚂蚁的源码,会发现都是在react-componet各组件上面进行的封装,而react-component里面用到了ReactDOM.unstable_renderSubtreeIntoContainer这个方法,但react官网API里面并没有把这个给列出来,估计是一个不太正式的API,但还是希望react把这个API放出来,因为很多情况下确实需要用到。
PS. 听说facebook正在研究react专门针对dom的处理方式,还是期待下的
总结:
toast.info('我是toast')