表单↓
❤ 受控组件
判断是否受控组件:看表单内的值是否受到state的控制
作用:将表单的可变状态数据和state都保存在state中 方便进行管理
为什么要做成受控组件:因为在提交订单时 需要收集用户的数据 并提交 受控组件能很方便的收集用户数据 非受控组件代码较为冗余
设置步骤:
- 给input添加value属性并和state中对应的数据进行绑定
value= {this.state.数据名称}
让其表单输入框的数据和state相连接 - 给input设置onChange事件
onChange = {e => this.事件名称({ txt: e.target.value})}
在事件处理函数中 e.target.value拿到输入的值 作用是让表单后续更新改动也可以监听到数值的变化 达到响应更新 - state只能公共setState来修改 通过this.setState将获取到的最新数值赋值给state 达到检测数据变化后更新数据的操作
// 文本框
Class Hello extends React.Component {
state = {
txt: ''
}
// 事件处理程序
handleTxt = (e) => {
this.setState() {
txt: e.target.value
}
}
render() {
return (
<input type="text" value={this.state.txt} onChange={this.handleTxt()}}</input>></input>
)
}
}
// 优化 使用一个事件处理程序来处理多个表单
Class Hello extends React.Component {
state = {
txt: '',
content: '',
city: 'xx1',
isChecked: false
}
// 事件处理程序
handleTxt = (e) => {
this.setState() {
txt: e.target.value
}
}
// 优化后的事件处理程序
handleForm = (e) => {
// 首先获取dom对象
const target = e.target
// checkbox是特殊value类型 所以需要判断类型来获取
// 为了保证能拿到所有类型的value
const value = target.type === 'checkbox'
? target.checked
: target.value
// 获取设置的name
const name = target.name
// 通过settState修改state
this.setState({
// 此处的name相当于一个变量
[name]:value
})
}
render() {
return (
<div>
<!-- 文本框 -->
<input type="text"
value={this.state.txt}
onChange={this.handleTxt()}}> </input>
<br/>
<!-- 富文本框 -->
<textarea name="content"
value={this.state.content}
onChange={this.handleForm}> </textarea>
<br/>
<!-- 下拉框 -->
<select name="city"
value={this.state.city}
onChange={this.handleForm}>
<option value="sh">xx1</option>
<option value="bj">xx2</option>
<option value="gz">xx3</option>
</select>
<br/>
<!-- 复选框 -->
<input type="checkbox"
name="isChecked"
checked={this.state.isChecked}
onChange={this.handleForm} />
</div>
)
}
}
// 渲染类组件
ReactDOM.render(<App />,document.getElementById('root'))
// name做了什么:做了区分 未来更新对应的状态
// 为什么name能做到更新未来对应的状态
// 因为每个表单都使用了一个名为name的外包装 包装内存入着各种名称和state一一对应 当触发每个不同的表单会触发里面对应不同的value 所以当触发的是txt文本表单 此时的name就是txt中的value
非受控组件 Ref
通过ref拿到dom对象来获取数据改变数据
// 类组件中
constructor() {
super()
// constructor中创建ref
this.ref = React.createRef()
}
// 事件处理程序
getText = (e) => {
// 将拿到的数值打印到控制台中
console.log(this.txtRef.current.value)
}
render() {
return (
<div>
<!-- 通过绑定ref拿到value -->
<input type='text' ref={this.ref}></input>
<button onClick={this.getText()}></button>
</div>
)
}
❤ props 组件之间通信
组件是封闭的一个单独的个体 数据都是私有的 但是在组合组件实现一些功能时 不可避免的需要让组件之间进行数据的传递 这种时候就要使用到组件通信
作用:接受传递给组件的数据
特点:
- 可以传递任意数据类型 数组、属性、函数、标签 非字符串的需要
{}
包裹 - 是个只读属性 不能修改数值 能使用setState修改嘛?
- 在类组件的constructor中不能直接通过this.props接收 需要通过constructor形参来接收 因为实例对象在生成时就会调用 此时的props未定义
// 类组件
class Hello extends React.Component {
constructor(props) {
super(props)
// 这样才能再constructor中拿到props
console.log(this.props)
}
}
如何传递数据: 通过给要传递数据的组件标签 添加属性
ReactDOM.render(<hello name="jack" age={18} />,document.getElementById('root'))
// 传递非字符串的数据 都用{}包裹 规则和表达式差不多
如何接收数据: 函数组件通过形参props接收数据 类组件通过this.props接收
// 函数形式
// 函数通过形参接收
Hello = (props) => {
console.log(props)
// { name:'jack', age:18}
return (
<div>接收到的数据: {props.name,props.age}</div>
)
}
// 类组件形式
class Hello extends React.Component {
render() {
// 类组件接收使用this.props
return (
<div>接收到的数据:{this.props.age}</div>
)
}
}
* 父子传值
父子传递的不限于组件的类型 只要是组件包含组件都能传递
// 父类组件
class Father extends React.component {
// 父组件的私有数据 要被传递的数据
state = {
userName:'一点点'
}
render() {
return (
<div>
<!-- 通过给子组件的的标签绑定父组件state私有数据 -->
子组件标签: <Child name= {this.state.userName} />
</div>
)
}
}
// 子函数组件 子组件就能通过props接收到数据了
const Child = (props) => {
return <div>子组件接收到的数据: {props.name} </div>
}
* 子传父
父组件向包裹的子组件标签传递一个回调函数
子组件在组件内部触发这个回调函数 将需要被传递的自身私有数据通过回调函数的形参传递给父组件
总结:哪个父组件需要子组件的数据 就提供一个回调函数接收
// 父组件
class Father extends React.conponent {
// 父组件的私有数据
state = {
fatherMsg:''
}
// 父组件定义的回调函数 由子组件触发 父组件不触发
getChildMsg = data => {
console.log('子组件传递过来的参数',data)
// 将子组件传递来的数据存入到父组件私有数据中使用
this.setState({
fatherMsg: data
})
}
render() {
return (
<div>
子组件传递来的数据:<span>{this.state.father}</span>
子组件标签: <Child getFather(this.getChildMsg()) />
</div>
)
}
}
// 子组件
class Child extends React.component {
// 子组件的私有数据 即将传递给父组件的数组
state = {
msg:'我是子组件的数据'
}
// 子组件的事件处理程序
childMsg = () => {
// 子组件通过this.props拿到父组件传递过来的和回调函数
this.props.getFather(this.state.msg)
}
render() {
return {
<!-- 子组件再点击时触发调用传递参数函数 -->
<button οnclick={this.childMsg()}></button>
}
}
}
* 兄弟传值
什么是兄弟传值:两个组件中的相互传值 需要通过最近的父组件这个中转站
状态提升:将两个子组件中的状态存储到父组件中 称为状态提升
原理:是子传父 和父传子的结合 因为私有状态数据提升到了父组件的私有状态中 所以当子组件传递给父组件数据 并父组件修改了传递过来的数据 另一个链接这个私有数据的子组件会自然而然发生改变
// 父组件
class Father extends React.Component {
// 父组件的私有数组 提供共享
state = {
count: 0
}
// 父组件提供的修改数据方法
onFatherCount = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
// 此处是子组件2是主动那一方 子组件1是被动的一方
<div>
<Child1 count={this.state.count} />
<Child2 onIncrement={this.onFatherCount} />
</div>
)
}
}
// 函数子组件1
const Child1 = props => {
// 子组件1通过props拿到父组件的私有数据
return (
<div>
<h1>计数器 {props.count}</h1>
</div>
)
}
// 函数子组件2
const Child2 = props => {
return (
// <!-- 子组件2事件处理程序 -->
<div>
<button onClick={() => props.onIncrement()}>+1按钮</button>
</div>
)
}
// 渲染组件 因为子组件嵌套在父组件中 所以此时只需要渲染父组件
ReactDOM.render(<Father />, document.getElementById('root'))
* Context 祖传
作用:在层级过多时 需要跨组件传递私有数据
实际运用场景:主题的颜色 一般各个组件都需要 层级比较复杂时 就可以使用context
// 创建这个Context 解构出得到两个需要的属性
// Provider:提供数据 包裹根组件
// Consumer:消费数据 包裹接受数据的结构
const {Provider, Consumer} = React.createContext()
// 父组件
class App extends React.Component {
// 传递多个数据时要怎么弄?
state = {
yanse: 'pink'
}
render() {
return (
// 要传递的值 写在Provider标签的value属性中
<Provider value="pink">
<div>
<Node />
</div>
</Provider>
)
}
}
// 嵌套关系 APP->Node->SubNode->Child
// 子组件 Node
const Node = props => {
return (
<div>
Node组件
<SubNode />
</div>
)
}
// 子组件 SubNode
const SubNode = props => {
return (
<div>
SubNode 组件
<Child />
</div>
)
}
// 子组件 Child
const Child = props => {
// 子组件作为子子子组件 但是拿到了父组件的value值 达成跨组件传值
return (
<div>
<Consumer>{data => <span>我是Child节点:{data} </span>}</Consumer>
</div>
)
}
// 渲染父组件
ReactDOM.render(<App />, document.getElementById('root'))
children属性
当组件标签变成双标签 包裹了其他节点会自动生成子节点 children属性
作用:可以通过{props.children}
属性能拿到所有子节点 并将其利用差值表达式将这个组件标签的子节点全部渲染到页面上
class App extends React.Component {
render() {
return (
<div>App组件</div>
{this.props.children}
)
}
}
// 渲染App组件
// 组件标签在基础学到过 标签中可以让任何类型的元素 标签 函数...
ReactDOM.render(<App><span>App的子节点<span>子节点的子节点</span></span></App>, document.getElementById('root'))
props校验
作用:是第三方插件 校验props外来传入数据的格式数据类型等 在传入的数据对象不符合要求时给出明确的错误 让组件在使用更加明确
-
安装
npm i props-types
-
导入
import PropTypes from 'prop-types'
-
使用
组件名.propTypes={}
-
校验规则通过
PropType
对象来指定// 导入PropType import PropType from 'prop-types' // 函数组件 const App = (props) => { return ( <div> <h1> {props.colors} </h1> <h2> {props.name} </h2> <h3> {props.user}</h3> </div> ) } // 常见类型 App.PropTypes ={ // 设置colors属性为array类型 假如传入的不是数组类型 会给出报错 colors: PropTypes.array } // 必选 isRequired App.PropTypes={ name:propTypes.string.isRequired } // 特定对象 shape App.PropTypes={ user:propTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }) } // 常见类型: array、bool、func、number、object、string // React元素类型: element // 必选: isRequired // 特定对象: shape({})
更多查看官方手册约束规则
defaultProps 默认值
作用:在没有传入props数值时生效的数据 为了不影响代码的基本运行
使用场景:分页组件 当分页组件默认展示多少条条数时 就是使用的默认值
// 创建函数组件
const App = (props) => {
return (
<div>
默认值: {props.pageSize}
</div>
)
}
// 设置默认值
App.defaultProps = {
pageSize: 10
}
// 当传入pageSize属性值时 会覆盖默认值 以传入的数值为准
ReactDOM.render(<App pageSize ={20}/>, document.getElementById('root'))