有小伙伴想造一个UI组件库的轮子,在React项目里,像message这样的需要额外插入节点的组件要怎么极(最)简(懒)的实现呢?
功能
- 支持message.warn ()、message.success()方式调用
- 页面可能同时存在多个Message
- 定时消失
实现 MessageWrapper 组件
可能会有多个Message同时存在,因此先实现一个容器组件,用来维护一个内容列表,并且支持增加和销毁。
- 通过 list 维护Message组件列表数据
- Message会定时消失,onHide在消失的时候将数据清理掉
- 实现了一个add方法给外部调用
class MessageWrapper extends React.Component {
state = {
list: []
}
add = (params = {content: '', type: 'info', key: ''}) => {
this.setState({
list: this.state.list.concat([params])
})
}
handleHide = (msg) => {
this.setState({
list: this.state.list.filter(item => item != msg)
})
}
render() {
return <div className="message-wrapper">
{
this.state.list.map(item => <Message onHide={this.handleHide.bind(this, item)} {...item}></Message>)
}
</div>
}
}
复制代码
实现 Message 组件
我是用一个动画来控制组件的显示和隐藏的,监听onAnimationEnd,动画结束的时候触发onHide清理DOM。
function Message (props) {
return <div className="message">
<div className="message__content" onAnimationEnd={props.onHide}>
<span className={`message__icon ${props.type}`}></span>
{props.content}
</div>
</div>
}
复制代码
CSS3动画来控制显示和隐藏
我是通过一个动画来控制的,在需要设置message停留时间或停留逻辑的时候不便于扩展,这里可以将动画分成出现和消失两个部分,通过setTImeout来控制。
.ani {
animation: message 2s linear 1;
}
@keyframes message {
0%, 100% {
opacity: 0;
transform: translateY(-5px);
}
5%, 95% {
opacity: 1;
transform: translateY(0);
}
}
复制代码
将Wrapper节点插入页面并生成message对象
1、在页面里插入一个容器节点
2、通过ReactDOM.render方法将 class 组件渲染到页面,并返回组件的实例。这里只能用class组件,pure组件或hooks组件是没法通过返回实例的方式来访问内部方法的!
React官方文档不建议直接使用render函数返回的引用了,理由是未来的版本组件渲染可能变成异步。不过只要React不更新,咱还是怎么香怎么用~
3、闭包返回message对象!
export const message = (function() {
let container = document.getElementById('message-container')
if (!container) {
container = document.createElement('div')
container.setAttribute('id', 'message-container')
document.body.appendChild(container)
}
const messageWrapper = ReactDOM.render(
<MessageWrapper />,
container
);
return {
warn: (content) => {
messageWrapper.add({
key: getUniqueKey(),
content,
type: 'warn'
})
},
error: (content) => {
messageWrapper.add({
key: getUniqueKey(),
content,
type: 'error'
})
},
success: (content) => {
messageWrapper.add({
key: getUniqueKey(),
content,
type: 'success'
})
},
}
})()
复制代码
总结
- 组件里实现的Wrapper里可以放各种弹层组件,需要的话,可以抽离出来。到这里,我终于知道为什么半天没搜到Antd插入节点的代码了,原来不在Antd这个包里,而是引入了一个 rc-notification 的包。
- ReactDOM.render方法会返回class组件的实例,但建议使用calllback ref的方式异步获取。
- 需要的话还可以补一个destroy方法,ReactDOM支持销毁组件:ReactDOM.unmountComponentAtNode(container)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END