react组件(2)
有状态与无状态组件
有状态组件
意思是这个组件能够获取储存改变应用或组件本身的状态数据,在React当中也就是state,一些比较明显的特征是我们可以在这样的组件当中看到对this.state的初始化,或this.setState方法的调用等等。
无状态组件
这样的组件一般只接收来自其他组件的数据。一般这样的组件中只能看到对this.props的调用,通常可以用函数定义组件的方式声明。它本身不会掌握应用的状态数据,即使触发事件,也是通过事件处理函数传递到其他有状态组件当中再对state进行操作。
class StatefulLink extends React.Component { //构造函数中设置初始化状态 constructor(props){ super(props) this.state={ active: false } } //点击事件触发,将状态取反 this.serState触发重新渲染 handleChick(){ this.setState({ active:!this.state.active }) } //返回元素 render(){ return <a style={{color: this.state.active?'red':'black'}} onClick ={this.handleChick.bind(this)} >点击会记录状态变色</a> } } //将组件StatefulLink渲染到root上 ReactDOM.render(<StatefulLink/>,document.getElementById('root')) //无状态控件 class StatelessLink extends React.Component{ constructor(props){ super(props); } handleClick(){ this.props.handleClick(this.props.router) } render(){ const active=this.props.activeRouter=== this.props.router; return ( <li> <a style={{color:active?'red':'black' }} onClick={this.handleClick.bind(this)} > 点击 </a> </li> ) } } class Nav extends React.Component{ constructor(){ super() //初始化时设置activeRouter值为home this.state={activeRouter:'home'} } handleSwitch(router){ this.setState({activeRouter:router}) } render(){ return( <ul> <StatelessLink activeRouter={this.state.activeRouter} router='home' handleClick={this.handleSwitch.bind(this)} /> <StatelessLink activeRouter={this.state.activeRouter} router='blog' handleClick={this.handleSwitch.bind(this)} /> <StatelessLink activeRouter={this.state.activeRouter} router='about' handleClick={this.handleSwitch.bind(this)} /> </ul> ) } } ReactDOM.render(<Nav />, document.getElementById('root2'))
通过ReactDOM渲染组件 nav,在constructor方法中设置this.state标记默认选择,render返回react元素,由多个statelesslink组件组成,并绑定router属性的值与点击事件,activeRouter动态取当前初始化
设置的home。
nav中返回的元素由StatelessLink组件组成,因此在返回之前会进到StatelessLink中的constructor和render方法返回react元素,StatelessLink在返回之前定义了一个变量用于判断选中项控制变色。
第一次加载控件, ul中的三个StatelessLink元素 会全部走一遍class StatelessLink 组件的定义,因为nav中在构造函数中设定了当前活动的路由为home,作为唯一一个可用满足active判断的元素
(相当于后台代码中的循环判断),因此加载完成后默认选择第一项标红。StatelessLink 触发点击事件时,会先触发handleSwitch 重新设置当前选中项然后在重新渲染,单个li属于无状态组件,通过nav
容器组合之后需要一个状态来标记。
受控与非受控组件
受控组件
一般涉及到表单元素时我们才会使用这种分类方法,在后面一节课程表单及事件处理中我们还会再次谈论到这个话题。受控组件的值由props或state传入,用户在元素上交互或输入内容会引起应用state的改变。在state改变之后重新渲染组件,我们才能在页面中看到元素中值的变化,假如组件没有绑定事件处理函数改变state,用户的输入是不会起到任何效果的,这也就是“受控”的含义所在。
非受控组件
类似于传统的DOM表单控件,用户输入不会直接引起应用state的变化,我们也不会直接为非受控组件传入值。想要获取非受控组件,我们需要使用一个特殊的ref属性,同样也可以使用defaultValue属性来为其指定一次性的默认值。
class ControlledInput extends React.Component{ constructor(){ super(); this.state={value:'请输入...'}; } handlechange(event) { this.setState({value:event.target.value}) } render(){ return( <lable> 输入的值是: <input type="text" value ={this.state.value} onChange={e=>this.handlechange(e) } /> </lable> ); } } class UncontrolledInput extends React.Component { constructor(){ super(); } handleChange(){ console.log("Uncontrolled change:", this.input.value); } render(){ return( <label> 不可控的文本框 <input type="text" defaultValue="请输入..." ref={input => (this.input = input)} onChange={() => this.handleChange()} /> </label> ) } } ReactDOM.render(<controlledInput/>,document.getElementById('root'))
ReactDOM.render(<UncontrolledInput/>,document.getElementById('root2'))
controlledInput与UncontrolledInput 区别就在于用户输入不会直接引起应用state的变化
//用一个函数处理多个输入 class ContactEdit extends React.Component{ constructor(){ super(); this.state={ contact:{ firstName: 'Bolun', lastName: 'Yu', phone: '123123123' } } this.handleChangeFor=propertyName=>event=>{ const {contact} =this.state; const newContact = { ...contact, [propertyName]: event.target.value }; this.setState({contact:newContact}) } } render(){ return ( <div> <input type="text" onChange={this.handleChangeFor("firstName")} value={this.state.contact.firstName} /> <input type="text" onChange={this.handleChangeFor("lastName")} value={this.state.contact.lastName} /> <input type="text" onChange={this.handleChangeFor("phone")} value={this.state.contact.phone} /> </div> ) } }
组合与继承
class ContactEdit extends React.Component { constructor() { super(); this.state = { contact : { firstName: 'Bolun', lastName: 'Yu', phone: '123123123' } } this.handleChangeFor = propertyName => event => { const { contact } = this.state; const newContact = { ...contact, [propertyName]: event.target.value }; this.setState({ contact: newContact }); }; } render() { return ( <div> <input type="text" onChange={this.handleChangeFor("firstName")} value={this.state.contact.firstName} /> <input type="text" onChange={this.handleChangeFor("lastName")} value={this.state.contact.lastName} /> <input type="text" onChange={this.handleChangeFor("phone")} value={this.state.contact.phone} /> </div> ); } }