第二章 React面向组件编程

基本理解和使用

自定义组件

  • 定义组件

    • 工厂函数组件(简单组件)
    function MyComponent () {return <h2>工厂函数组件(简单组件)</h2>
    }
    

    没有状态的组件

    • ES6类组件(复杂组件)
    class MyComponent2 extends React.Component {render () {console.log(this) // MyComponent2的实例对象return <h2>ES6类组件(复杂组件)</h2>}
    }
    
  • 渲染组件标签

ReactDOM.render(<MyComponent />, document.getElementById('example1'))

注意事项

组件名必须首字母大写

虚拟DOM元素只能有一个根元素

虚拟DOM元素必须有结束标签

组件三大属性(state)

  • state是组件对象最重要的属性, 值是对象(可以包含多个数据)

  • 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)

操作

  • 初始化状态
constructor (props) {super(props)// 初始化状态this.state = {isLikeMe: true}
  • 读取某个状态->绑定this为组件对象
this.change = this.change.bind(this)
  • 更新状态->组件界面更新
this.setState({isLikeMe: !this.state.isLikeMe
})

组件三大属性(props)

  • 每个组件对象都会有props(properties的简写)属性

  • 组件标签的所有属性都保存在props中

操作

  • 内部读取某个属性值
this.props.propertyName
  • 对props中的属性值进行类型限制和必要性限制
Person.propTypes = {name: PropTypes.string.isRequired,sex: PropTypes.string,age: PropTypes.number
}
  • 扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>

... 的作用:

打包

function fn(...as) {} fn( 1,2,3)

解包

const arr1 =[1,2,3] const arr2 = [6, ...arr1, 9]
  • 默认属性值
Person.defaultProps = {sex: '男',age: 18
}
  • 组件类的构造函数
constructor (props) {super(props)console.log(props) // 查看所有属性
}

props和state属性区别

props state
用于定义外部接口 用于记录内部状态
赋值在于外部世界使用组件 赋值在于组件内部
不改变组件值 让组件来修改的
不能在constructor中设置默认值 只能在constructor中设置默认值
从组件外部向组件内部传递数据, 组件内部只读不修改 组件自身内部可变化的数据
none setState修改值为异步的

组件三大属性(refs与事件处理)

//定义组件class MyComponent extends React.Component {constructor(props) {super(props) // 调用父类(Component)的构造函数//console.log(this)// 将自定义的函数强制绑定为组件对象this.showInput = this.showInput.bind(this) // 将返回函数中的this强制绑定为指定的对象, 并没有改变原来的函数中的this}// 自定义的方法中的this默认为nullshowInput() {// alert(this) //this默认是null, 而不是组件对象// 得到绑定在当前组件对象上的input的值alert(this.msgInput.value)}handleBlur(event) {alert(event.target.value)}render() {return (<div><input type="text" ref={input => this.msgInput = input}/>{' '}<button onClick={this.showInput}>提示输入数据</button>{' '}<input type="text" placeholder="失去焦点提示数据" onBlur={this.handleBlur}/></div>)}}// 渲染组件标签ReactDOM.render(<MyComponent/>, document.getElementById('example'))

refs属性

  • 组件内的标签都可以定义ref属性来标识自己

    • <input type="text" ref={input => this.msgInput = input}/>

    • 回调函数在组件初始化渲染完或卸载时自动调用

  • 在组件中可以通过this.msgInput来得到对应的真实DOM元素

  • 作用: 通过ref获取组件内容特定标签对象, 进行读取其相关数据

事件处理

  • 通过onXxx属性指定组件的事件处理函数(注意大小写)

    • React使用的是自定义(合成)事件, 而不是使用的原生DOM事件

    • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)

  • 通过event.target得到发生事件的DOM元素对象

<input onFocus={this.handleClick}/>handleFocus(event) {event.target  //返回input对象
}

注意

  • 组件内置的方法中的this为组件对象

  • 在组件类中自定义的方法中this为null

    • 强制绑定this: 通过函数对象的bind()

    • 箭头函数(ES6模块化编码时才能使用)

组件的组合

流程

  • 拆分组件: 拆分界面,抽取组件
  • 实现静态组件: 使用组件实现静态页面效果(只有静态界面,没有动态数据和交互)
  • 实现动态组件
    • 实现动态显示初始化数据
    • 交互功能(从绑定事件监听开始)

问题1:数据保存在哪个组件内?
看数据是某个组件需要(给它),还是某些组件需要(给共同的父组件)
问题2:需要在子组件中改变父组件的状态
子组件中不能直接改变父组件的状态
状态在哪个组件,更新状态的行为就应该定义在哪个组件
解决:父组件定义函数,传递给子组件,子组件调用

<script type="text/babel">class App extends React.Component {constructor(props) {super(props)// 初始化状态this.state = {todos: ['吃饭', '睡觉', '打豆豆']}this.addTodo = this.addTodo.bind(this)}addTodo(todo) {const {todos} = this.statetodos.unshift(todo)//更新状态this.setState({todos})}render() {const {todos} = this.statereturn (<div><h1>Simple TODO List</h1><Add count={todos.length} addTodo={this.addTodo}/><List todos={todos}/></div>)}}class Add extends React.Component {constructor(props) {super(props);this.add = this.add.bind(this)}add() {// 读取输入数据const todo = this.todoinput.value.trim()// 检查合法性if (!todo) {return}// 保存到todosthis.props.addTodo(todo)// 清除输入this.todoinput.value = ''}render() {return (<div><input type="text" ref={input => this.todoinput = input}/><button onClick={this.add}> add #{this.props.count + 1}</button></div>)}}Add.propTypes = {count: PropTypes.number.isRequired,addTodo:PropTypes.func.isRequired}class List extends React.Component {render() {const {todos} = this.propsreturn (<ul>{todos.map((todo, index) => {return <li key={index}>{todo}</li>})}</ul>)}}List.propTypes = {todos: PropTypes.array.isRequired,}// 渲染应用组件标签ReactDOM.render(<App/>, document.getElementById('example'))</script>

收集表单数据

  • 包含表单的组件分类

    • 受控组件: 表单项输入数据能自动收集成状态

    • 非受控组件: 需要时才手动读取表单输入框中的数据

效果

<script type="text/babel">class LoginForm extends React.Component {constructor(props) {super(props)// 初始化状态this.state = {username: ''}this.handleSubmit = this.handleSubmit.bind(this)this.handleChange = this.handleChange.bind(this)}handleChange(event) {this.setState({username: event.target.value})}handleSubmit(event) {alert(`准备提交的用户名为: ${this.state.username}, 密码:${this.pwdInput.value}`)// 阻止事件的默认行为: 提交表单event.preventDefault()}render() {return (<form onSubmit={this.handleSubmit} action="/test"><label>用户名:<input type="text" value={this.state.username} onChange={this.handleChange}/></label>&nbsp;<label>密码:<input type="password" ref={(input) => this.pwdInput = input}/></label>&nbsp;<input type="submit" value="登陆"/></form>)}}ReactDOM.render(<LoginForm/>, document.getElementById('example'))
</script>

组件生命周期

生命周期表

效果

  • 组件对象从创建到死亡它会经历特定的生命周期阶段

  • React组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调

<script type="text/babel">class Fade extends React.Component {constructor (props) {super(props)console.log('constructor(): 创建组件对象')this.state = {opacity: 1}this.removeComponent = this.removeComponent.bind(this)}componentWillMount () {console.log('componentWillMount(): 初始化将要挂载')}componentDidMount () {// 在此方法中启动定时器/绑定监听/发送ajax请求console.log('componentDidMount(): 初始化已经挂载')// 保存到当前组件对象中this.intervalId = setInterval(function () {// 得到当前opacitylet {opacity} = this.state// 更新opacityopacity -= 0.1if(opacity<=0) {opacity = 1}// 更新状态this.setState({opacity})}.bind(this), 200)}componentWillUpdate () {console.log('componentWillUpdate(): 将要更新')}componentDidUpdate () {console.log('componentDidUpdate(): 已经更新')}componentWillUnmount () {// 清除定时器/解除监听console.log('componentWillUnmount(): 将要被移除')clearInterval(this.intervalId)}removeComponent () {ReactDOM.unmountComponentAtNode(document.getElementById('example'))}render() {console.log('render() 渲染组件')return (<div><h2 style={{opacity:this.state.opacity}}>{this.props.content}</h2><button onClick={this.removeComponent}>不活了</button></div>)}}ReactDOM.render(<Fade content="react学不会, 怎么办?"/>, document.getElementById('example'))
</script>

生命周期流程图

  • 组件对象从创建到死亡它会经历特定的生命周期阶段

  • React 组件对象包含一系列的钩子函数(生命周期回调函数),在生命周期特定时刻回调

  • 我们在定义组件时,可以重写特定的生命周期回调函数,做特定的工作

生命周期详述

  • Mount:挂载过程,第一次将组件插入到真实 DOM
  • Update:更新过程,组件被重新渲染
  • Unmount:卸载过程,被移出真实 DOM

生命周期流程

1)创建阶段(第一次初始化渲染显示)

ReactDOM.render()

  • constructor():super(props) 指定 this,this.state={} 创建初始化状态(getDefaultProps、getInitialState)

  • componentWillMount()

    :组件将要挂载到页面上

    • 可以在这里调用 setState() 方法修改 state
  • render():创建虚拟 DOM 但是还没有挂载上去

  • componentDidMount()

    :已经挂载到页面上(初始界面已经渲染完毕)

    • 可以在这里通过 this.getDOMNode() 来进行访问 DOM 结构
    • 可以在这里发送 ajax 请求
    • 添加监听器/订阅

2)运行阶段(二次渲染)

父组件传递的 props 发生更新,就会调用 componentWillReceiveProps()

  • componentWillReceiveProps(nextProps):当子组件接受到 nextProps 时,不管这个 props 与原来的是否相同都会调用

props 改变或者调用 this.setState() 方法更新 state,都会触发组件的更新,调用后面的钩子函数

  • shouldComponentUpdata(nextProps, nextState):接收一个新的 props 和 state,返回true/false,表示是否允许更新

    • 通常情况下为了优化,需要对新的 props 以及 state 和原来的数据作对比,如果发生变化才更新

调用 this.forceUpdate() 方法会直接进入 componentWillUpdate。跳过 shouldComponentUpdate()

  • componentWillUpdate():将要更新
  • render():重新渲染
  • componentDidUpdate():已经完成更新

除了首次 render 之后调用 componentDidMount,其它 render 结束之后都是调用 componentDidUpdate

3)销毁阶段(移除组件)

执行 ReactDOM.unmountComponentAtNode(containerDom) 用来使组件从真实 DOM 中卸载(开始销毁阶段)

  • componentWillUnmount():组件将要被移除时(移出前)回调

    • 一般在 componentDidMount 里面注册的事件需要在这里删除

重要勾子

  • render():初始化渲染时或更新渲染时调用
  • componentDidMount():开启监听,可以初始化一些异步操作:启动定时器/发送 ajax 请求
  • componentWillUnmount():做一些收尾工作,如:清理定时器
  • componentWillReceiveProps():当组件接收到(父元素传递的)新的 props 属性前调用

虚拟DOM与DOM Diff算法

效果

<script type="text/babel">class HelloWorld extends React.Component {constructor(props) {super(props)this.state = {date: new Date()}}componentDidMount () {setInterval(() => {this.setState({date: new Date()})}, 1000)}render () {console.log('render()')return (<p>Hello, <input type="text" placeholder="Your name here"/>!&nbsp;<span>It is {this.state.date.toTimeString()}</span></p>)}}ReactDOM.render(<HelloWorld/>,document.getElementById('example'))
</script>

基本原理图

[React] 尚硅谷 -- 学习笔记(二)相关推荐

  1. [React] 尚硅谷 -- 学习笔记(一)

    第一章 React入门 React基本认识 用于构建用户界面的 JavaScript 库(View) 官网 英文官网: https://reactjs.org/ 中文官网: https://doc.r ...

  2. [React] 尚硅谷 -- 学习笔记(七)

    第七章 react-ui 最流行的开源React UI组件库 material-ui(国外) 官网 GitHub ant-design(国内蚂蚁金服) PC官网 GitHub 移动官网 GitHub ...

  3. [React] 尚硅谷 -- 学习笔记(六)

    第六章 react-router4 理解 react-router react的一个插件库 专门用来实现一个SPA应用 基于react的项目基本都会用到此库 SPA 单页Web应用(single pa ...

  4. [React] 尚硅谷 -- 学习笔记(四)

    第四章 react ajax 理解 React本身只关注于界面, 并不包含发送ajax请求的代码 前端应用需要通过ajax请求与后台进行交互(json数据) react应用中需要集成第三方ajax库( ...

  5. [React] 尚硅谷 -- 学习笔记(三)

    第三章 react应用(基于react脚手架) 使用create-react-app创建react应用 react脚手架 xxx 脚手架:用来帮助程序员快速创建一个基于 xxx 库的模板项目 包含了所 ...

  6. [React] 尚硅谷 -- 学习笔记(五)

    第五章 总结 组件间通信 通过props传递 共同的数据放在父组件上, 特有的数据放在自己组件内部(state) 通过props可以传递一般数据和函数数据, 只能一层一层传递 一般数据–>父组件 ...

  7. Java 基础 第3阶段:高级应用——尚硅谷学习笔记(含面试题) 2023年

    Java 基础 第 3 阶段:高级应用--尚硅谷学习笔记(含面试题) 2023 年 Java 基础 第 3 阶段:高级应用--尚硅谷学习笔记(含面试题) 2023 年 第 9 章 异常处理 9.1 异 ...

  8. B站MySQL(尚硅谷)学习笔记

    B站MySQL基础(尚硅谷)学习笔记 最近在学习数据库技术,并且把视频中的知识点进行了汇总,字数较多,仅供参考. 会持续更新 欢迎读者提出问题与错误,一起交流~ 视频前几集所讲述的基本知识: DB:数 ...

  9. 8-zookeeper算法基础(尚硅谷学习笔记)

    目录 拜占庭将军问题 paxos算法 paxos算法流程 情况一 情况二 情况1 情况2 ZAB协议 ZAB协议内容 消息广播 过程 可能出现的问题 崩溃恢复 异常假设 leader选举 数据恢复 C ...

最新文章

  1. 2011年云计算发展趋势的五大预测
  2. switch-case和if-else可互换时
  3. 分布式系统架构与云原生—阿里云《云原生架构白皮书》导读
  4. 桌面鼠标手写输入法_「桌面分享」工作娱乐两不误,花费7万打造的桌面都有些啥?...
  5. 机器学习中的数学知识(part1)
  6. LLVM与Codegen技术
  7. MDP马尔可夫决策过程(二)
  8. 一个用js写的接口http调试程序
  9. Excel2016保存文件闪退(在安装了Visio后)
  10. MBA-day12 逻辑学-关系判断
  11. matlab中solver函数_Matlab中solve函数用法详解
  12. SDN是什么东东???
  13. Markdown常用样式
  14. SEO优化-SEO具体方法,SEO干货分享
  15. 场景设计题 汇总 (一)
  16. mysql 3306_允许远程链接mysql,开放3306端口
  17. PCB应力应变测试-ICT、FCT治具应力把控标准
  18. 了不起的Node.js: 将JavaScript进行到底(Web开发首选,实时,跨多服务器,高并发)...
  19. cuteftp不能连接虚拟机的解决方法
  20. 解决绝对定位留下来的空白

热门文章

  1. android倒计时动画特效,Android仿活动时分秒倒计时效果
  2. 部编版是什么版本_教材部编版和人教版的区别
  3. excel和mysql php_php将mysql数据库和Excel相互导入和导出的方法
  4. huffman图像编码C语言,Huffman编码的c语言实现
  5. MYSQL 批量Insert ID顺序生成(仿雪花算法)
  6. NETSTAT 指令详解
  7. Android SDK实例之Snake游戏深入解析(一)
  8. 概率图模型更进一步的知识点
  9. javaweb项目得执行过程及servlet得请求(Httprequest)和响应(Httpresponse)
  10. 8.5-Day1T1--Asm.Def 谈笑风生