React复习(4)

本文介绍了React中的受控组件和非受控组件概念,受控组件通过state管理和同步数据,而非受控组件直接操作DOM。此外,还讲解了如何通过ref属性获取DOM节点,包括不同方式的ref使用以及如何在子组件间进行引用转发,提高代码的便捷性和可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(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'))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无非是我

不必打赏,欢迎指正

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值