5 收集表单数据
5.1 效果
需求:定义一个包含表单的组件
输入用户名密码后,点击登录提示输入信息
用受控组件方式写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2_受控组件</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-from,用于支持react操作dom -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写babel */
// 创建组件
class Login extends React.Component {
// 初始化状态
state = {
username: '',
password: ''
}
// 保存用户名到状态中
saveUsername = (event) => {
this.setState({username: event.target.value});
}
// 保存密码到状态中
savePassword = (event) => {
this.setState({password: event.target.value});
}
// 表单提交的回调
handleSubmit = (event) => {
event.preventDefault(); // 阻止表单提交
const {username, password} = this.state;
alert(`你输入的用户名是${username},你输入的密码是:${password}`);
}
render() {
return (
<form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
用户名<input onChange={this.saveUsername} type="text" name="username"/>
密码<input onChange={this.savePassword} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login/>, document.getElementById('test'));
</script>
</body>
</html>
5.2 理解
包含表单的组件分类
-
受控组件
-
非受控组件
5.3 高阶函数
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
1.若A函数。接收的参数是一个函数,那么A就可以称之为高阶函数
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
常见的高阶函数有:Promise、setTimeout、arr.map()等等
5.4 函数柯里化
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
function sum(a) {
return (b) => {
return (c) => {
return a + b + c;
}
}
}
const result = sum(1)(2)(3);
console.log(result);
6 组件的生命周期
1 练习
需求**😗*定义组件实现以下功能:
1. 让指定的文本做显示 / 隐藏的渐变动画
2. 从完全可见,到彻底消失,耗时2S
3. 点击“不活了”按钮从界面中卸载组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello_react</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-from,用于支持react操作dom -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写babel */
// 创建组件
// 生命周期回调函数 => 生命周期钩子函数 => 生命周期函数 => 生命周期钩子
class Life extends React.Component {
state = {opacity: 1};
death = () => {
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'));
}
// 组件挂载完毕
componentDidMount() {
this.timer = setInterval(() => {
// 获取原状态
let {opacity} = this.state;
// 减小0.1
opacity -= 0.1;
if (opacity <= 0) opacity = 1;
this.setState({opacity});
}, 200);
}
// 组件将要卸载
componentWillUnmount() {
// 清空定时器
clearInterval(this.timer);
}
// render调用的时机:初始化渲染、状态更新之后
render() {
return (
<div>
<h2 style={{opacity: this.state.opacity}}>React学不会怎么办?</h2>
<button onClick={this.death}>不活了</button>
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Life/>, document.getElementById('test'));
</script>
</body>
</html>
2 理解
-
组件从创建到死亡它会经历一些特定的阶段。
-
React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
-
我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
3 生命周期流程图(旧)
生命周期的三个阶段(旧)
1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
-
constructor()
-
componentWillMount()
-
render()
-
componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
-
shouldComponentUpdate()
-
componentWillUpdate()
-
render()
-
componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
注意:
-
shouldComponentUpdate
不写默认返回的是true
,如果返回false那么setState就不能通过后面阶段。 -
forceUpdate
强制更新,不管shouldComponentUpdate
返回的是什么。 -
要想实现改变了状态但是不调用render,不更新,可以利用阀门
shouldComponentUpdate
去控制。 -
没有对状态进行变化,但是需要更新,那么就走
forceUpdate
这条路。 -
父组件render时,
componentWillReceiveProp
s第一次不调用
setState路线
和forceUpdate
路线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>hello_react</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-from,用于支持react操作dom -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/*
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() =====> 必须使用的一个
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() ====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
*/
// 创建组件
class Count extends React.Component {
constructor(props) {
console.log("Count---constructor");
super(props);
// 初始化状态
this.state = { count: 0 };
}
// 加1按钮的回调
add = () => {
// 获取原状态
const { count } = this.state;
// 更新状态
this.setState({ count: count + 1 });
};
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'));
}
// 强制更新按钮的回调
force = () => {
this.forceUpdate();
}
// 组件将要挂载的钩子
componentWillMount() {
console.log('Count---componentWillMount');
}
// 组件挂载完毕的钩子
componentDidMount() {
console.log('Count---componentDidMount');
}
// 组件将要卸载的钩子
componentWillUnmount() {
console.log('Count---componentWillUnMount');
}
// 控制组件更新的“阀门”
shouldComponentUpdate() {
console.log('Count---shouldComponentUpdate');
return false;
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('Count---componentWillUpdate');
}
// 组件更新完毕的钩子
componentDidUpdate() {
console.log('Count---componentDidUpdate');
}
render() {
console.log('Count---render');
const { count } = this.state;
return (
<div>
<h2>当前求和为:{this.state.count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
</div>
);
}
}
// 父组件A
class A extends React.Component {
state = {carName: '奔驰'};
changeCar = () => {
this.setState({carName: '奥拓'});
}
render() {
return (
<div>
<div>我是A组件</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName}/>
</div>
)
}
}
// 子组件B
class B extends React.Component {
// 组件将要接收新的props钩子
componentWillReceiveProps(props) {
console.log('B--componentWillReceiveProps', props);
}
// 控制组件更新的“阀门”
shouldComponentUpdate() {
console.log('B--shouldComponentUpdate');
return true;
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('B---componentWillUpdate');
}
// 组件更新完毕的钩子
componentDidUpdate() {
console.log('B---componentDidUpdate');
}
render() {
console.log('B---render');
return (
<div>我是B组件,接收到的车是:{this.props.carName}</div>
)
}
}
// 渲染组件
ReactDOM.render(<A />, document.getElementById("test"));
</script>
</body>
</html>
4 生命周期流程图(新)
生命周期的三个阶段(新)
1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
-
constructor()
-
getDerivedStateFromProps
-
render()
-
componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
-
getDerivedStateFromProps
-
shouldComponentUpdate()
-
render()
-
getSnapshotBeforeUpdate
-
componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
注意:
新版本中以下三个生命周期钩子需要加上UNSAFE_
-
UNSAFE_componentWillMount
-
UNSAFE_componentWillUpdate
-
UNSAFE_componentWillReceiveProps
新的生命周期钩子与旧的相比:即将废弃3个钩子:componentWillMount,componentWillUpdate,componentWillReceiveProps。新增两个钩子:getDerivedStateProps、getSnapshotBeforeUpdate
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>3_react生命周期(新)</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
<!-- 引入react-from,用于支持react操作dom -->
<script
type="text/javascript"
src="../js/17.0.1/react-dom.development.js"
></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="text/babel">
// 创建组件
class Count extends React.Component {
constructor(props) {
console.log("Count---constructor");
super(props);
// 初始化状态
this.state = { count: 0 };
}
// 加1按钮的回调
add = () => {
// 获取原状态
const { count } = this.state;
// 更新状态
this.setState({ count: count + 1 });
};
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById("test"));
};
// 强制更新按钮的回调
force = () => {
this.forceUpdate();
};
// 若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props, state) {
console.log('getDerivedStateFromProps', props, state);
return 'atguigu';
}
// 在更新之前获取快照
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate');
return null;
}
// 组件挂载完毕的钩子
componentDidMount() {
console.log("Count---componentDidMount");
}
// 控制组件更新的“阀门”
shouldComponentUpdate() {
console.log("Count---shouldComponentUpdate");
return true;
}
// 组件更新完毕的钩子
componentDidUpdate(preProps, preState, snapshotValue) {
console.log("Count---componentDidUpdate", preProps, preState, snapshotValue);
}
render() {
console.log("Count---render");
const { count } = this.state;
return (
<div>
<h2>当前求和为:{this.state.count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>
不更改任何状态中的数据,强制更新一下
</button>
</div>
);
}
}
// 渲染组件
ReactDOM.render(<Count count={199}/>, document.getElementById("test"));
</script>
</body>
</html>
5 重要的勾子
-
render:初始化渲染或更新渲染调用
-
componentDidMount:开启监听, 发送ajax请求
-
componentWillUnmount:做一些收尾工作, 如: 清理定时器
6 即将废弃的勾子
-
componentWillMount
-
componentWillReceiveProps
-
componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
7 虚拟DOM与DOM Diffing算法
基本原理图
验证diffing算法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>验证diff算法</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="text/babel">
class Time extends React.Component {
state = {date: new Date()}
componentDidMount () {
setInterval(() => {
this.setState({
date: new Date()
})
}, 1000)
}
render () {
return (
<div>
<h1>hello</h1>
<input type="text"/>
<span>
现在是:{this.state.date.toTimeString()}
<input type="text"/>
</span>
</div>
)
}
}
ReactDOM.render(<Time/>,document.getElementById('test'))
</script>
</body>
</html>
key的作用
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>key的作用</title>
</head>
<body>
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/*
经典面试题:
1). react/vue中的key有什么作用?(key的内部原理是什么?)
2). 为什么遍历列表时,key最好不要用index?
1. 虚拟DOM中key的作用:
1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,
随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
(2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
根据数据创建新的真实DOM,随后渲染到到页面
2. 用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2. 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
仅用于渲染列表用于展示,使用index作为key是没有问题的。
3. 开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果确定只是简单的展示数据,用index也是可以的。
*/
/*
慢动作回放----使用index索引值作为key
初始数据:
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
初始的虚拟DOM:
<li key=0>小张---18<input type="text"/></li>
<li key=1>小李---19<input type="text"/></li>
更新后的数据:
{id:3,name:'小王',age:20},
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
更新数据后的虚拟DOM:
<li key=0>小王---20<input type="text"/></li>
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
-----------------------------------------------------------------
慢动作回放----使用id唯一标识作为key
初始数据:
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
初始的虚拟DOM:
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
更新后的数据:
{id:3,name:'小王',age:20},
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
更新数据后的虚拟DOM:
<li key=3>小王---20<input type="text"/></li>
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
*/
class Person extends React.Component{
state = {
persons:[
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
]
}
add = ()=>{
const {persons} = this.state
const p = {id:persons.length+1,name:'小王',age:20}
this.setState({persons:[p,...persons]})
}
render(){
return (
<div>
<h2>展示人员信息</h2>
<button onClick={this.add}>添加一个小王</button>
<h3>使用index(索引值)作为key</h3>
<ul>
{
this.state.persons.map((personObj,index)=>{
return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li>
})
}
</ul>
<hr/>
<hr/>
<h3>使用id(数据的唯一标识)作为key</h3>
<ul>
{
this.state.persons.map((personObj)=>{
return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/></li>
})
}
</ul>
</div>
)
}
}
ReactDOM.render(<Person/>,document.getElementById('test'))
</script>
</body>
</html>