欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > React:类组件(下)

React:类组件(下)

2025/3/16 4:11:50 来源:https://blog.csdn.net/Au_ust/article/details/146183476  浏览:    关键词:React:类组件(下)

表单中的非受控组件

设置默认值,使用ref来从 DOM 节点中获取表单数据,defaultValue设置默认值,不去控制后续的更新

import React, { Component } from 'react';class App extends Component {myUsername = React.createRef()handleReset = () => {if (this.myUsername.current) {this.myUsername.current.value = ''; // 直接操作 DOM 元素的值,不能在onClick里修改,因为ref是只读的}};render() {return (<div><h1>登录页</h1><input type="text" ref={this.myUsername}  defaultValue={'孩子们我不想上学'}/><button onClick={()=>{console.log(this.myUsername.current.value)}}>登录</button><button onClick={()=>this.handleReset()}>重置</button></div>);}
}export default App;
非受控组件可以减少你的代码量,也不会受外部的控制

表单的受控组件

受控组件其实就是把ref的值设为一个状态,input在React里其实更像一个组件,而不是一个普通的input标签

import React, { Component } from 'react';class App extends Component {myUsername = React.createRef()handleReset = () => {if (this.myUsername.current) {this.myUsername.current.value = ''; // 直接操作 DOM 元素的值,不能在onClick里修改,因为ref是只读的}}state = {usename:'august'
}render() {return (<div><h1>登录页</h1><input type="text" ref={this.myUsername}  defaultValue={this.state.usename}/><button onClick={()=>{console.log(this.myUsername.current.value)}}>登录</button><button onClick={()=>this.handleReset()}>重置</button></div>);}
}export default App;

但是这时候没有设置onChange事件,通过onChange监听input,需要在input察觉到值变化以后立即更新状态

这种行为和原生js里的onInput事件更相似

对于 <input type="text"> 和 <textarea>onChange 在元素失去焦点(blur)时触发。

受控组件的优点是实现了父子通信,在重新渲染的同时,还可以把更改的状态传给其他组件

import React from 'react'
import axios from 'axios'
class Cinema extends React.Component{constructor() {super()this.state = {cinemaList: [],myText:''}//用axios请求数据axios.get('https://apis.netstart.cn/maoyan/index/moreCinemas?day=2021-11-12&offset=0&limit=20&districtId=-1&lineId=-1&hallType=-1&brandId=-1&serviceId=-1&areaId=-1&stationId=-1&item&updateShowDay=true&reqId=1636710166221&cityId=1&lat=23.135636443326355&lng=1').then(res => {console.log(res)this.setState({cinemaList: res.data})}).catch(err => {console.log(err)})}//生命周期函数更适合发送ajax请求render() {return (<div><input value={this.state.myText} onChange={(e) => {this.setState({myText:e.target.value})}}  />{this.getCinemaList().map(item => <dl key={item.cinemaId}><dt>{ item.title}</dt><dd>{ item.location}</dd></dl>)}</div> )}getCinemaList() {return this.state.cinemaList.filter(item => item.title.includes(this.state.myText)||item.location.includes(this.state.myText))}
}
export default Cinema

受控组件没有操作dom,而非受控组件操作dom

使用受控组件修改之前写的todolist:
import React, { Component } from "react" 
class App extends Component{a=100state = {list: [{id:1,text:'111',isChecked:false},{ id: 2, text: '222',isChecked:false },{id:3,text:'333',isChecked:false}],myText: '',}render(){return (<div><input value={this.state.myText} onChange={(e) => {this.setState({myText:e.target.value})}} />{/* //把ref绑定在input上 */}<button onClick={this.handleClick}>add</button><ul>{this.state.list.map((item, index) => <li key={item.id}> <input type="checkbox" checked={this.state.list.isChecked} onChange={()=>this.handleCheck(index)} /><span style={{textDecoration:item.isChecked?'line-through':''}}> {item.text}</span><button onClick={() => this.handleDel(index)}>delete</button></li>)
}</ul>{/* { this.state.list.length?null:<div>暂无待办事项</div>} */}{/* { this.state.list.length===0&&<div>暂无待办事项</div>} */}<div className="hidden">暂无待办事项</div></div>)}handleCheck(index) {//console.log(index)let newList = [...this.state.list]newList[index].isChecked = !newList[index].isCheckedthis.setState({list:newList})}handleClick = (e) => {//this.state.list.push(this.myref.current.value)不能直接修改状态let newList = [...this.state.list]newList.push({id:this.state.list.length+1,text: this.state.myText,isChecked:false})this.setState({list: newList,myText:''})}handleDel = (index) => {console.log('del',index)let newList =this.state.list.slice()newList.splice(index, 1)this.setState({list:newList})}
}
export default App

修改的部分主要是把组件设置为受控的,并且增添的删除时加上删除线的功能,主要是通过控制组件的checked的状态来设置文字的css样式,用map方法把index传给onChange里的回调函数,并且把复选框作为list里每个元素的一个属性,

注意 : 另一种说法(广义范围的说法), React 组件的数据渲染是否被调用者传递的 props 完全控制,控制则
为受控组件,否则非受控组件。

父子通信

为什么要进行父传子通信

父传子是为了更好的实现组件的复用,父亲需要提不同的要求给子组件,如果没有属性的传递,就无法实现复用

子传父是为什么?

状态是局部的,使用的时候需要传递;如果需要实现一个抽屉的效果,点击导航栏的按钮,触发一个列表,再触发关闭列表的效果;如果将导航栏和列表做成两个组件的时候,父组件App在使用这两个组件的时候,根据状态(isShow)来确定是否显示列表,这个状态肯定要写在父组件里,可是按钮被写在子组件里,按钮是否被点击的状态此时就需要传递给父组件,这就是子传父的必要性

像这样:

import React, { Component } from 'react';class Navbar extends Component{render() {return (<div style={{background:'red'}}><button onClick={() => {console.log('子通知父,让父的isShow取反,这里的this.props就是父组件给子组件传递的回调函数', this.props)this.props.event()}}>click</button><span>Navbar</span></div>)}
}
class Slidebar extends Component{render() {return (<div style={{ background: 'yellow' }}><ul><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li><li>qq</li></ul></div>)}
}class App extends Component {state = {isShow:false}render() {return (<div><Navbar event={() => {console.log('父组件定义event事件')}}></Navbar>{this.state.isShow&&<Slidebar></Slidebar>}</div>);}
}export default App;

event这个回调函数是父传给子的,子拿到之后就可以执行

父传子通过属性(一些字符串什么的)传递,子传父通过回调函数(父如果传给子一个回调函数就证明我们需要子传父,需要子在自己的作用域里调用父传过来的回调函数)来执行(之前没理解这句话,现在真理解了)

import React, { Component } from 'react';class Navbar extends Component{render() {return (<div style={{background:'red'}}><button onClick={() => {console.log('子通知父,让父的isShow取反,这里的this.props就是父组件给子组件传递的回调函数', this.props)this.props.event()}}>click</button><span>Navbar</span></div>)}
}
class Slidebar extends Component{render() {return (<div style={{ background: 'yellow' }}><ul><li>qq</li><li>qq</li><li>qq</li><li>qq</li></ul></div>)}
}class App extends Component {state = {isShow:false}handleEvent = () => {this.setState({isShow:!this.state.isShow})}render() {return (<div><Navbar event={this.handleEvent}></Navbar>{this.state.isShow&&<Slidebar></Slidebar>}</div>);}
}export default App;

现在就可以通过点击子组件的按钮控制父组件的列表是否显示了

非受控组件的实例

父组件触发事件更改状态以后会引起render的渲染,但是更改不了子组件里的state

import React, { Component } from 'react';
import '../../2-advance/css/01-卖座.css'
class Tabbar extends Component {state={list: [{id:1,  text:'电影'},{id: 2, text: '影院'},{id: 3, text: '我的'},],current: 0      }render() {// this.state.current = this.props.parentcurrent// this.setState({//     current:this.props.parentcurrent// })不能这么写,这么写render调用setState,setState更改完状态又调用renderreturn (<div><ul>{this.state.list.map((item, index) =><li key={item.id} className={this.state.current === index ? 'active' : ''} onClick={() => this.handleClick(index)}>{item.text}</li>)}   </ul></div>);}handleClick(index) {console.log(index)this.setState({current: index})   this.props.myEvent(index)}}export default Tabbar;
也就是说render察觉到状态被其父组件改变了会渲染,但是只会在子组件第一次执行的时候创建列表在列表里声明current的值,而之后无论current怎么改变,只会触发渲染,而不会真正的改变列表里的current这个状态
这算是状态组件比较不方便的地方就是提升代码维护的难度

状态组件、无状态组件、受控组件、非受控组件的区别

概念特点适用场景
受控组件值由 React 状态管理,数据流单向需要严格控制的表单或输入场景
非受控组件值由 DOM 管理,数据流双向简单表单或直接操作 DOM 场景
状态组件组件内部有状态逻辑,可以是类组件或函数组件需要管理复杂状态的场景
无状态组件组件内部没有状态逻辑,完全依赖 props纯展示型或逻辑简单的场景

受控组件的实例

核心是剥离子组件的状态,通过父组件的通信传递

只由自己控制的,就定义在state里,由外界控制的,通过props传入,记住这条规则就行了
接收props
为了减少状态的使用,并且让父组件通过更改props的属性值来控制子组件,应该用props而不是state
这是类组件的做法,如果是函数式组件就没有这么麻烦,因为属性可以直接当参数传递:
子组件:
const Tabbar=(props) => {function handleClick(index){props.myEvent(index)}if (!props.list) {return <div>No data available</div>}return (<div><ul>{props.list.map((item, index) =><li key={item.id} className={props.current === index ? 'active' : ''} onClick={() =>handleClick(index)}>{item.text}</li>)}   </ul></div>);
}export default Tabbar;

父组件:

import React, { Component } from "react" 
import './css/01-卖座.css'
import Film from './maizuo受控/Film'
import Center from './maizuo受控/Center'
import Cinema from './maizuo受控/Cinema'
import Tabbar from './maizuo受控/Tabbar'
import Navbar from './maizuo受控/Navbar'
import { current } from "@reduxjs/toolkit"
class App extends Component{state = {current: 0,list: [{id:1,  text:'电影'},{id: 2, text: '影院'},{id: 3, text: '我的'},],}which() {switch (this.state.current) {case 0:return <Film></Film>case 1:return <Cinema></Cinema>case 2: return <Center></Center>default:return null}}render(){return (<div><Navbar myEvent={() => {console.log('Navbar')this.setState({current:2})}}></Navbar>{/* {其实感觉这里也可以拿路由来写} */}{this.which()}<Tabbar myEvent={(index )=>{console.log('父组件定义')this.setState({current:index})}   //通过属性传给Tabbar} current={this.state.current} list={this.state.list}></Tabbar></div>)}}
export default App

讨厌类组件的指来指去

父子通信的表单域组件

感觉没什么多说的,就是受控组件

import React, { Component } from 'react';class Field extends Component {render() {return (<div style={{ background: 'yellow' }}><label >{ this.props.label}</label><input type={this.props.type} onChange={(e) => { this.props.onChangeEvent(e.target.value) }} />{/* //每次点击按钮触发回调函数并且把e.target.value传递回去,子传父通过回调函数 */}</div>);}
}class App extends Component {render() {return (<div><h1>登录页面</h1><Field label='用户名' type='text' onChangeEvent={(value)=>{console.log(value)}}></Field><Field label='密码' type='password' onChangeEvent={(value) => {console.log(value) }}></Field><button >登录</button><button >取消</button></div>);}
}export default App;

这是具体的设置属性为state

     <input type={this.props.type} onChange={(e) => { this.props.onChangeEvent(e.target.value) }} value={this.props.value} />{/* //每次点击按钮触发回调函数并且把e.target.value传递回去,子传父通过回调函数 */}<Field label='用户名' type='text' onChangeEvent={(value) => {// console.log(value)this.setState({username:value})//读取本地存储的信息当作默认值属性传递给子组件}} value={this.state.username}></Field>

这里把this.state.value传过去

ref版表单域组件

访问ref

通过ref绑定的Field,获取Field组件的函数clear:
import React, { Component ,} from 'react';class Field extends Component {state={value:''}clear() {this.setState({value:''})}render() {return (<div style={{ background: 'yellow' }}><label >{ this.props.label}</label><input type={this.props.type} onChange={(e) => {this.setState({//没有父传子子传父value:e.target.value})}} />{/* //每次点击按钮触发回调函数并且把e.target.value传递回去,子传父通过回调函数 */}</div>);}
}class App extends Component {username=React.createRef()password=React.createRef()render() {return (<div><h1>登录页面</h1><Field label='用户名' type='text' ref={this.username}></Field><Field label='密码' type='password'ref={this.password}></Field><button onClick={() => {console.log(this.username.current.state.value,this.password.current.state.value)}} >登录</button><button onClick={() => {this.username.current.clear()}}>取消</button></div>);}
}export default App;

情况的时候把value值设置回去:

<input type={this.props.type} onChange={(e) => {this.setState({//没有父传子子传父value: e.target.value})}} value={this.state.value} />

避免暴露的使用方法,一些方法可以封装出来:

  setValue(value) {this.setValue({value:value})
}<div style={{ background: 'yellow' }}><label >{ this.props.label}</label><input type={this.props.type} onChange={(e) => {this.setValue(e.target.value)}} value={this.state.value} />{/* //每次点击按钮触发回调函数并且把e.target.value传递回去,子传父通过回调函数 */}</div>
低耦合好维护,直来直去,不像受控组件绕来绕去

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词