(1)受控组件(官方推荐)
受控组件根据数据状态变化改变,数据驱动变化。其中变化过程react帮我们做了,也就是同步dom,我们只需要改变数据即可,不需要直接做dom操作
受控表单
class App extends React.Component {
state = {
name: "wjk",
age: 18,
single: false,
instruction: '无',
hobby: 'playing'
}
//每个事件都有一个事件对象,input的e里会包含所有input的属性值
handleSubmit = (e) => {
// 从form表单搜集数据
console.log('form data:', this.state);
// 提交
// 阻止默认行为
e.preventDefault();
}
handleChange = (e) => {
let target = e.target
//根据表单name属性来查找的
let key = target.name
let type = target.type
let value = type== 'checkbox' ? target.checked : target.value
console.log(key, value)
//把表单中的值传到state中
//动态赋值 详见es6中的计算属性名称
this.setState({
[key]: value // Object.assign({},{name:'sss'},{age:18})
})
}
render() {
return (
<div>
{/*
react中不自动双向绑定,要自己传数据
将state传到input里用属性绑定value
将input传回state里用事件绑onChange,在onChange中调用setState改变state
*/}
<form onSubmit={this.handleSubmit}>
姓名:<input type="text" name="name" value={this.state.name} onChange={this.handleChange} />
年龄<input type="number" name="age" value={this.state.age} onChange={this.handleChange} />
是否单身:<input type="checkbox" name="single" value={this.state.single} onChange={this.handleChange} />
简介:<textarea name="instruction" value={this.state.desc} onChange={this.handleChange} />
爱好:<select name="hobby" value={this.state.hobby} id="" onChange={this.handleChange}>
<option value="eating">吃</option>
<option value="playing">玩</option>
<option value="sleeping">睡</option>
</select>
{/*submit点击后会触发onSubmit合成事件*/}
<input type="submit" value="提交" />
</form>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("app"))
(2)非受控组件
非受控组件不受数据控制,直接操作dom,通过ref获取dom,类似于传统的html操作
通过ref属性获取dom共有三种方式
class Clock extends React.Component {
render() {
return (
<div>
{/*1:在refs这个空对象创建一个ref属性,里给ref传一个key值*/}
<h2 ref='timeRef'>{new Date().toLocaleTimeString()}</h2>
</div>
)
}
componentDidMount() {
console.log(this.refs);
setInterval(() => {
{/*通过refs.来获取值*/ }
this.refs.timeRef.innerHTML = new Date().toLocaleTimeString();
}, 1000);
}
}
class Clock extends React.Component {
render() {
return (
<div>
{/*2:直接利用ref,不再利用refs对象,e就是真实DOM对象*/}
<h2 ref={e => this.timeRef2 = e}>{new Date().toLocaleTimeString()}</h2>
<h2 ref={(element) => {
this.timeRef2 = element
}}>{new Date().toLocaleTimeString()}</h2>
</div>
)
}
componentDidMount() {
console.log(this.refs);
setInterval(() => {
this.timeRef.innerHTML = new Date().toLocaleTimeString();
}, 1000);
}
}
//3:利用React.createRef()里面的current属性来装真实DOM*/
class Clock extends React.Component {
constructor(props) {
super(props)
//挂载属性
this.timeRef = React.createRef()
}
render() {
return (
<div>
{/*使用constructor里挂载的属性,ref会把dom自动传给current*/}
<h2 ref={this.timeRef}>{new Date().toLocaleTimeString()}</h2>
</div>
)
}
componentDidMount() {
console.log(this.refs);
setInterval(() => {
this.timeRef.current.innerHTML = new Date().toLocaleTimeString();
}, 1000);
}
}
ReactDOM.render(<Clock />, document.getElementById('app'))
非受控表单
class Form extends React.Component {
constructor() {
super()
this.inputRrf3 = React.createRef()
}
handleClick = () => {
//第一种
console.log(this.refs.inputRef.value);
//第二种
console.log(this.inputRrf2.value);
//第三种
console.log(this.inputRrf3.current.value);
}
//第四种:通过onChange属性实时打印值
handleChange = (e) => {
console.log(e.target.value);
}
render() {
return (
<div>
<input type="text" ref="inputRef" />
<input type="text" ref={e => this.inputRrf2 = e} />
<input type="text" ref={this.inputRrf3} />
<input type="text" onChange={this.handleChange} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById('app'))
其中原理都是一样的
ref属性获取dom原理:
将真实DOM放到容器this.refs={}里,
通过createElement创建虚拟DOM 新增ref属性
ReactDOM.render把虚拟DOM同步到真实DOM上
如果有ref属性,则把ref属性对应的真实DOM放进去
{ref属性对应的值作为key :真实DOM}
最后再通过this.refs.key.innerHTML操作
在生命周期componentDidMount中虚拟DOM已经转化为真实DOM
非受控表单
class Form extends React.Component {
constructor() {
super()
this.inputRrf3 = React.createRef()
}
handleClick = () => {
//第一种
console.log(this.refs.inputRef.value);
//第二种
console.log(this.inputRrf2.value);
//第三种
console.log(this.inputRrf3.current.value);
}
//第四种:通过onChange属性实时打印值
handleChange = (e) => {
console.log(e.target.value);
}
render() {
return (
<div>
<input type="text" ref="inputRef" />
<input type="text" ref={e => this.inputRrf2 = e} />
<input type="text" ref={this.inputRrf3} />
<input type="text" onChange={this.handleChange} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById('app'))
非受控方法实现获取焦点
//进入页面自动获取焦点,失去焦点触发事件
class Form extends React.Component {
constructor() {
super()
this.inputRrf3 = React.createRef()
}
handleClick = () => {
console.log(this.inputRrf3.current.value);
}
handleBlur = (e) => {
console.log(e);
console.log(e.target);
alert(e.target.value)
}
render() {
return (
<div>
<input type="text" onBlur={this.handleBlur} ref={this.inputRrf3} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
componentDidMount() {
this.inputRrf3.current.focus()
}
}
ReactDOM.render(<Form />, document.getElementById('app'))
(3)forward(引用转发)
实际上forwardRef就是一个自动转发ref的技术,用起来更加方便简洁
//通过引用转发的方式来获取子组件dom
//4:ref属性接收父组件传来的地址
function ChildInput(props,ref){
return(
<div>
<h2>child input</h2>
{/*5:检测到ref属性,而且值是current容器的地址,则把dom赋值给current*/}
<input type="text" ref={ref}/>
</div>
)
}
//1:把子组件包裹起来,返回一个新的组件
//实际上forwardRef就是一个自动转发ref的技术
const RefChildInput = React.forwardRef(ChildInput)
class FatherForm extends React.Component {
constructor() {
super()
//2:绑定属性使current容器可用
this.inputRef = React.createRef();
}
handleClick = () => {
//6:利用current操作dom
this.inputRef.current.focus()
}
render() {
return (
<div>
{/*3:利用引用转发把current地址转发给子组件*/}
<RefChildInput ref={this.inputRef}/>
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<FatherForm />, document.getElementById('app'))
接下里是不使用forward,利用回调函数来实现
//通过回调函数获取子组件的dom
class ChildInput extends React.Component{
render(){
return(
<div>
<h2>child input</h2>
{/*3:子组件渲染时发现有ref属性而且值是函数,则把dom对象传给函数*/}
<input type="text" ref={this.props.callbackRef}/>
</div>
)
}
}
function ChildInput(props){
return(
<div>
<h2>child input</h2>
<input type="text" ref={props.callbackRef}/>
</div>
)
}
class FatherForm extends React.Component {
//1:声明一个函数,在函数中模拟第二种ref获取dom的放法
callbackRef=(e)=>{
//4:e就是dom对象,把它赋值到this.inputRef属性上
this.inputRef = e
}
handleClick = () => {
//若用current能获取子组件实例但是获取不到dom
//5.通过this.inputRef属性对dom进行操作
this.inputRef.focus()
}
render() {
return (
<div>
{/*2:把函数传给子组件,函数是引用类型所以直接传地址*/}
<ChildInput callbackRef={this.callbackRef}/>
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<FatherForm />, document.getElementById('app'))