目录
- 前言:
- 一、组件的基本理解和使用
-
- 1. 函数式组件
- 2. 类式组件
- 3. 注意事项
- 4. 渲染函数式组件标签的基本流程
- 5. 渲染类组件标签的基本流程
- 二、组件三大核心属性 1:state
-
- 1. 代码示例
- 2. 效果展示
- 3. 注意
- 4. 设置状态:setState
- 三、 组件三大核心属性 2:props
-
- 1. 函数式组件代码示例
- 2. 类式组件代码示例
- 3. 效果展示
- 4. props 属性的特点:
- 5. 对 props 进行限制
- 四、组件三大核心属性 3:refs与事件处理
-
- 1. 字符串形式的代码示例
- 2. 回调函数形式的代码示例
- 3. createRef 代码示例
- 4. 效果展示
- 5. refs 使用场景
- 总结:
前言:
在React中,组件实例的三大核心属性包含state、props、ref,通过这三大核心属性的使用,我们能够实现对组件的状态进行更新。
一、组件的基本理解和使用
1. 函数式组件
function MyComponent(){
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
ReactDOM.render(<MyComponent />,document.getElementById('test'))
2. 类式组件
class MyComponent extends React.Component{
render(){
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
ReactDOM.render(<MyComponent />,document.getElementById('test'))
3. 注意事项
- 组件名必须首字母大写
- 虚拟DOM元素只能有一个根元素
- 虚拟DOM元素必须有结束标签
4. 渲染函数式组件标签的基本流程
- React 解析了组件标签,找到对应的组件
- 发现这个组件是一个函数定义的,随后调用该函数,生成了一个虚拟DOM
- 最后将虚拟DOM转化为真实DOM,呈现在页面中;
5. 渲染类组件标签的基本流程
- React 解析了组件标签,找到对应的组件
- 发现这个组件是一个类定义的,随后new出来一个实例对象,并通过该实例调用原型上的render方法
- 将render()返回的内容生成了一个虚拟DOM
- 最后将虚拟DOM转化为真实DOM,呈现在页面中;
二、组件三大核心属性 1:state
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
1. 代码示例
<script type="text/babel">
// 1. 创建一个类式组件
class Weather extends React.Component{
// 构造器调用了几次? --- 实例化了几次,调用几次 --- 1次
constructor(){
super();
// console.log(this)
this.state = {isHot:false,wind:"微风"}
this.changeWeather = this.changeWeather.bind(this)
}
// render方法是放在原型上的
// render中的this是谁? -- 实例对象 <===> Weather组件实例对象
// render方法调用了几次? --- 1 + N
// 调用的流程:
// 1. 页面状态发生改变时,重新调用render方法来生成一个新的虚拟DOM
// 2. 通过diff算法来 对比 新旧虚拟DOM之间的差别,从而进行合并
// 3. 将对比合并过后的虚拟DOM 转换为真实DOM,挂载到页面的指定位置
render(){
// console.log('render中的this',this)
// onClick事件:首字母需要大写,由于changeWeather是作为onClick的事件回调,所以这里需要一个函数体,而不是函数执行后的结果
let {isHot,wind} = this.state
return <h2 onClick={this.changeWeather}>今天的天气很 {isHot? '炎热' : '凉爽' },{wind}</h2> // isHot
}
// changeWeather调用几次? --- 点几次调用几次
changeWeather(){
// 因为Babel编译,所以导致类中的方法默认开启局部的严格模式,所以这里的this是undefined
// console.log('changeWeather',this.state)
// 严重注意:状态(state)不可直接修改
// this.state.isHot = !this.state.isHot // 这里是一种错误写法
let {isHot} = this.state
this.setState({ isHot:!isHot }) // 这里的修改是一种合并,对比属性的变化,如果有赋新值,如果没有跳过;
}
}
// 2. 渲染到页面中的指定 DOM
// ReactDOM.render(虚拟DOM,真实DOM)
ReactDOM.render(<Weather />,document.getElementById('test'))
</script>
2. 效果展示
3. 注意
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this: 通过函数对象的bind()
- 箭头函数
- 状态数据,不能直接修改或更新
4. 设置状态:setState
setState(object nextState[, function callback])
不能在组件内部通过 this.state
修改状态,因为该状态会在调用 setState()
后被替换。
setState()
并不会立即改变 this.state
,而是创建一个即将处理的 state。setState()
并不一定是同步的,为了提升性能 React 会批量执行 state 和 DOM 渲染。
setState()
总是会触发一次组件重绘,除非在 shouldComponentUpdate()
中实现了一些条件渲染逻辑。
三、 组件三大核心属性 2:props
react 中说的单向数据流值说的就是 props,根据这一特点它还有一个作用:组件之间的通信。props 本身是不可变的,但是有一种情形它貌似可变,即是将父组件的 state 作为子组件的 props,当父组件的 state 改变,子组件的 props 也跟着改变,其实它仍旧遵循了这一定律:props 是不可更改的。
1. 函数式组件代码示例
<script type="text/babel">
function Person (props) {
let {name,age,sex} = props
return(
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
// 给指定的属性,添加默认值
Person.defaultProps = {
sex:'男'
}
// 1. 数据类型,是否应该有对应的限制?
// 2. 数据的数量,批量性传输的时候,可以使用展开运算符
// let speak = 'speak'
ReactDOM.render(<Person name="tom" age="18" sex="男"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="jock" age="19" sex="女"/>,document.getElementById('test2'))
const p = {name:'老王',age:22}
ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
</script>
2. 类式组件代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Props基本使用</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<!-- 引入 React 核心库 -->
<script src="../js/react.development.js"></script>
<!-- 引入 react-dom 用于支持 react 操作 DOM -->
<script src="../js/react-dom.development.js"></script>
<!-- 引入babel:1. ES6 ==> ES5 2. jsx ==> js-->
<script src="../js/babel.min.js"></script>
<script type="text/babel">
class Person extends React.Component{
render(){
let {name,age,sex} = this.props
console.log(this)
return(
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
// 1. 数据类型,是否应该有对应的限制?
// 2. 数据的数量,批量性传输的时候,可以使用展开运算符
ReactDOM.render(<Person name="tom" age="18" sex="男"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="jock" age="19" sex="女"/>,document.getElementById('test2'))
const p = {name:'老王',age:22,sex:'男'}
// console.log("@",{...p})
ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
</script>
</body>
</html>
3. 效果展示
需求: 自定义用来显示一个人员信息的组件
- 姓名必须指定,且为字符串类型;
- 性别为字符串类型,如果性别没有指定,默认为男
- 年龄为字符串类型,且为数字类型,默认值为18
4. props 属性的特点:
- 每个组件对象都会有props(properties的简写)属性
- 组件标签的所有属性都保存在props中
- 内部读取某个属性值:this.props.propertyName
- 作用:通过标签属性从组件外 向组件内传递数据(只读 read only)
- 对props中的属性值进行类型限制和必要性限制
5. 对 props 进行限制
- 引入
prop-type
库,这就是专门用于限制props
属性的一个库 - 导入
prop-type
库,到当前页面 - 根据
Person.propTypes = {}
进行限制
参考官方文档所示
// cli 脚手架引入方式
import PropTypes from "prop-types";
// js 引入方式
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
// 类式组件
class index extends Component {
static propTypes = {
// 限制 list,为数组类型,必填
list: PropTypes.array.isRequired,
// 限制 changeitem,为一个函数,必填
changeitem: PropTypes.func.isRequired,
// 限制 onDelete,为一个函数,必填
onDelete: PropTypes.func.isRequired,
}
render() {
const { list, changeitem, onDelete } = this.props
return (
<ul className="todo-main">
<Item list={list} changeitem={changeitem} onDelete={onDelete} />
</ul>
);
}
}
注意:
在 React v15.5 中PropTypes是react的内置对象,在 React v15.5 开始已弃用以下方式。
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
四、组件三大核心属性 3:refs与事件处理
Refs 是一个 获取 DOM节点或 React元素实例的工具。在 React 中 Refs 提供了一种方式,允许用户访问DOM 节点或者在render方法中创建的React元素。
在 React单项数据流中,props是父子组件交互的唯一方式。要修改一个子组件,需要通过的新的props来重新渲染。
但是在某些情况下,需要在数据流之外强制修改子组件。被修改的子组件可能是一个React组件实例,也可能是一个DOM元素。对于这两种情况,React 都通过 Refs的使用提供了具体的解决方案。
1. 字符串形式的代码示例
class Demo extends React.Component{
showData=()=>{
// console.log(this)
// 专款专用:每一个ref都是唯一的,存在this.refs中
let {ipt1} = this.refs
alert(ipt1.value)
}
showData2=()=>{
let {ipt2} = this.refs
alert(ipt2.value)
}
render(){
return(
<div>
<input ref="ipt1" type="text" placeholder="请输入数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref="ipt2" onBlur={this.showData2} type="text" placeholder="请输入数据" />
</div>
)
}
}
2. 回调函数形式的代码示例
class Demo extends React.Component{
showData=()=>{
let {input1} = this
alert(input1.value)
}
showData2=()=>{
let {input2} = this
alert(input2.value)
}
render(){
return(
<div>
{/* 这里是注释~ */}
<input ref={ c=>this.input1 = c} type="text" placeholder="请输入数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref={c=>this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
什么是回调函数?
- 自己写的函数
- 不是自己调用的
- 这个函数最后执行了
3. createRef 代码示例
class Demo extends React.Component{
/*
React.createRef() 返回一个容器,容量为1,只能存一个,多余会覆盖前者
这个容器中,可以去存储一个指定的DOM。
*/
// 专款专用:每一个自定义的ref标记只能给唯一的DOM使用
myRef = React.createRef() // 容量为1,只能存一个,多余会覆盖前者
myRef2 = React.createRef()
showData=()=>{
alert(this.myRef.current.value)
}
showData2=()=>{
alert(this.myRef2.current.value)
}
render(){
return(
<div>
<input ref={ this.myRef} type="text" placeholder="请输入数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
注意:
React 官方推荐使用 createRef 创建 ref 容器的方法
4. 效果展示
需求: 自定义组件, 功能说明如下:
- 点击按钮, 提示第一个输入框中的值
- 当第2个输入框失去焦点时, 提示这个输入框中的值
5. refs 使用场景
refs 通常适合在一下场景中使用:
- 对DOM 元素焦点的控制、内容选择或者媒体播放;
- 通过对DOM元素控制,触发动画特效;
- 通第三方DOM库的集成。
避免使用 refs 去做任何可以通过声明式实现来完成的事情。例如,避免在Dialog、Loading、Alert等组件内部暴露 open(), show(), hide(),close()等方法,最好通过 isXX属性的方式来控制。
总结:
欢迎大家加入我的社区,在社区中会不定时发布一些精选内容:https://bbs.csdn.net/forums/db95ba6b828b43ababd4ee5e41e8d251?category=10003
以上就是 React 面向组件编程(上),不懂得也可以在评论区里问我或私聊我询问,以后会持续发布一些新的功能,敬请关注。
我的其他文章:https://blog.csdn.net/weixin_62897746?type=blog