文章目录
组件
受控组件与非受控组件
非受控即 组件内部不存在状态,组件的状态由父组件传入;一般该类型组件用来做数据展示,但不可控制
非受控组件通常我们会写成纯函数想这样:
import React from 'react'
export default function UncontrollForm({user}){
return(
<div className="profile-form">
<div className="field">
<label>Name</label>
<input type="text"
readOnly
value={user.name}
/>
</div>
</div>
)
}
受控组件
该类型组件,接受父组件传入的数据后,若自身数据更新将数据传递给父组件并更新自身状态
我们一般会这样做:
import React from 'react'
export default class ArrowFncForm extends React.PureComponent{
constructor(props){
super(props);
this.handleChange=this.handleChange.bind(this);
}
handleChange(type,value){
//处理业务逻辑
console.log(type,'--------',value)
//传递给父组件
this.props.onChange(type,value);
}
render(){
const {user}=this.props;
return(
<div className="profile-form">
<div className="field">
<label>Name</label>
<input type="text"
value={user.name}
onChange={e=>this.handleChange('name',e.target.value)}
/>
</div>
<div className="field">
<label>E-mail</label>
<input type="text"
value={user.email}
onChange={e=>this.handleChange('email',e.target.value)}
/>
</div>
</div>
)
}
}
问题: 当多个受控组件时,我们会给处理函数加入type字段来区分,在onChange中要给函数传递参数我们只能用箭头函数包一层,即向上面那样…
但这样做也会导致 render 每次创建/替换箭头函数,接受这些函数的 内容也将重新呈现。
我们会这样修正:
import React from 'react'
export default class ArrowFncForm extends React.PureComponent{
constructor(props){
super(props);
this.handleNameChange=this.handleNameChange.bind(this);
this.handleEmailChange=this.handleEmailChange.bind(this);
}
handleNameChange(e){
//传递给父组件
this.props.onChange('name',e.target.value);
}
handleEmailChange(e){
//传递给父组件
this.props.onChange('email',e.target.value);
}
render(){
const {user}=this.props;
return(
<div className="profile-form">
<div className="field">
<label>Name</label>
<input type="text"
value={user.name}
onChange={this.handleNameChange}
/>
</div>
<div className="field">
<label>E-mail</label>
<input type="text"
value={user.email}
onChange={this.handleEmailChange}
/>
</div>
</div>
)
}
}
提示:有人可能会给 input 加上name属性,此时在处理函数中获取,这样会减少一些代码但本质和上面一样。该做法不在次讲述
优化form
原文传送
我们可以通过React context来将公用的事件进行传输,然后将输入组件包裹在HOC(高级组件)中以读取上下文。
示例如下:
// EntityData.jsx
const EntityDataContext = React.createContext();
export function withEntityData(Component) {
return function EntityDataComponent(props) {
return (
<EntityDataContext.Consumer>
{ entityProps => {
const source = props.source || entityProps.source;
const path = props.path;
const sourceValue = _.get(path, source);
const value = props.value !== undefined ?
props.value : sourceValue;
const onChange = props.onChange || entityProps.onChange;
return (
<Component
{ ...props }
source={ source }
path={ path}
value={ value } />
onChange={ onChange } />
);
}}
</EntityDataContext.Consumer>
);
};
}
export default class EntityData extends React.PureComponent {
render() {
return (
<EntityDataContext.Provider value={{
source: this.props.source,
onChange: this.props.onChange
}}>
{ this.props.children }
</EntityDataContext.Provider>
);
}
}
// StringInput.jsx
import React from 'react'
import {withEntityData} from '../EntityData'
class StringInput extends React.PureComponent{
constructor(props){
super(props);
this.handleChange=this.handleChange.bind(this);
}
handleChange(e){
this.props.onChange && this.props.onChange(e.target.value);
}
render(){
const {value}=this.props;
return(
<input
type='text'
value={value}
onChange={this.handleChange}
></input>
)
}
}
export default withEntityData(StringInput)
现在我们可以这样使用:
//ProfileForm.jsx
class ProfileForm extends React.PureComponent {
render() {
const { user, onChange } = this.props;
return (
<div className="profile-form">
<EntityData source={ user } onChange={ onChange }>
<StringField label="Name" path="name" />
<StringField label="E-mail" path="email" />
<StringField label="Phone" path="phone" />
</EntityData>
</div>
);
}
}