react入学习梳理及实现手风琴组件
react
特点
- 解决视图层问题
业务逻辑可配合Redux和GraphQL - 函数式
一个react组件就是一个原生javascript类或者是函数,可以利用函数式特性实现高阶组件(HOC) - JSX 类XML,原生js做条件渲染
对比
显示一个hello world
Angular
{% jsfiddle hangbale/j6wyy2xr html,js,result %}
React
class App extends React.Component {
render () {
return (
<h1>Hello, world</h1>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
);
相同点(react vs vue)
- 虚拟DOM
- 组件化
不同点
- 模板 - jsx与HTML,angular vue 将js与 html混合在一起,react将HTML放进js中。拓展性更好
- react更加社区活跃 生态圈更大
- react native
组件
1.函数式组件
无state,无生命周期,纯函数,只有props,性能比类声明式的好
易组合,可用来做基础的渲染的组件,或者容器组件
function Foo(props) {
return <div>Hello, {props.children}</div>;
}
2.类声明式(ES6)
最完善的方式,支持props,state,生命周期
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
3.非ES6类(需要引入create-react-class库) 在ES6中需要为方法手动绑定this,在这里就不需要了
var TickTock = createReactClass({
getDefaultProps: function () {
return {
name: 'TickTock'
};
},
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000);
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
生命周期
- 初始渲染
- getDefaultProps
- getInitialState
上面这两个周期在class式或者函数式的组件中没有,可直接属性赋值,而在使用createReactClass时需要用到 - componentWillMount
在render前调用一次,在其中调用setState无意义,不会有重复渲染 - render
组件必须包含这个钩子 - componentDidMount
DOM已经渲染,类似VUE中的mounted可调用setState,会触发重新渲染。这里可以操作DOM,发起ajax请求。
- 组件变化
- componentWillReceiveProps
props变化时触发,初始挂载时并不会触发 - shouldComponentUpdate
非常有用,用来性能优化,避免重复渲染 - componentWillUpdate
不能在这里调用setState会导致内存泄露 - componentDidUpdate
此时dom已经更新 - componentWillUnmount
这里可以做一些清理工作
实现手风琴组件
效果
<Accordion activeindex="1">
<Accordionitem classname="item1" header="one">lord I'm one</Collapseitem>
<Accordionitem classname="item2" header="two">lord I'm two</Collapseitem>
<Accordionitem header="three">lord I'm three</Collapseitem>
<Accordionitem header="four">lord I'm four</Collapseitem>
<Accordionitem header="five">lord I'm five hundred miles away from home</Collapseitem>
</Accordion>
支持设置默认展开项,自定义class,设置标题
思路
- 哪个子项active应该是在父级中通过state来控制,然后传给子项的props
- 每个子项应该有一个key,在点击时候,在回调函数中传入这个key,然后在父级上通过这个key修改state,更新DOM
- 由于子项的个数是不定的,而且不能修改子项的props,所以需要使用clone
Accordion实现
class Accordion extends Component {
constructor(props) {
super(props);
this.state = {
activeKey: this.props.activeindex || '0'
// 设置初始显示项,默认为第一个
}
}
setActiveKey(key) {
// 触发重新渲染
this.setState({
activeKey: key
})
}
render() {
let childs = this.props.children;
// 这样可以拿到模板代码中定义的所有子项目
let activeKey = this.state.activeKey;
let clonedEle = [];
childs.forEach((item, index) => {
let key = item.key || index;
let isActive = key == activeKey;
// 比较key 判断是否应该active
let newProps = {
...item.props,
key: item.key || new String(index),
onClick: () => this.setActiveKey(key),
// 绑定上下文到父级 这是一种父子组件通信的方式
isActive: isActive
}
clonedEle.push(React.cloneElement(item, newProps))
// cloneElement方法很重要 针对组合式的组件 动态修改props
})
return (
<div className="collapse-wrapper">
{clonedEle} // 花括号语法表示是一个react表达式 类似于vue中的{{}}
</div>
);
}
}
Accordionitem实现
class Accordionitem extends Component {
constructor(props) {
super(props);
this.state = {
isActive: this.props.isActive || false
}
}
componentWillReceiveProps (nextProps) {
// 处理子项被点击,父级向下传入新的props
if (this.props.isActive && nextProps.isActive) {
// 如果原先是active 并且又被再次点击 那么应该close
this.setState({
isActive: !this.state.isActive
})
return;
}
this.setState({
isActive: nextProps.isActive
// 接受父级传入的props并设置state
})
}
render() {
const childText = this.props.children;
let cls = 'collapse-item ';
if (this.props.classname) {
cls += this.props.classname;
}
if (this.state.isActive) {
cls += ' active';
}
let itemClick = this.props.onClick;
let header = this.props.header;
return (
<div className={cls} onClick={itemClick}>
<div className="collapse-item-header">{header}</div>
<div className="collapse-item-body">{childText}</div>
</div>
)
}
}