本文记录了作者在学习React的第二天的内容,主要包括以下几个部分:表单数据提交、高阶函数与函数柯里化、组件的生命周期
一、表单数据提交
Ref 非受控组件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/react/18.2.0/umd/react.production.min.js"></script>
<script
src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/prop-types/15.8.1/prop-types.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
class RefExample extends React.Component {
handleSubmit = (event) => {
console.log(event);
event.preventDefault();
alert(`你的用户名是${this.username.value},你输入的密码是${this.password.value}`)
}
render() {
return (
<form action="#" onSubmit={this.handleSubmit}>
<h1>表单提交</h1>
<input type="text" placeholder="姓名" name='username' ref={(c)=>{this.username=c}}/><br />
<input type="password" placeholder="密码" name='password' ref={(c)=>{this.password=c}} /><br />
<input type='submit'></input><br />
</form>
)
}
}
ReactDOM.render(<RefExample />, document.getElementById('example'));
</script>Ref 受控组件
<!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-dom,用于支持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">
//创建组件
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 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>高阶函数_函数柯里化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>高阶函数_函数柯里化</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持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">
//#region
/*
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){
return(b)=>{
return (c)=>{
return a+b+c
}
}
}
*/
//#endregion
//创建组件
class Login extends React.Component{
//初始化状态
state = {
username:'', //用户名
password:'' //密码
}
//保存表单数据到状态中
saveFormData = (dataType)=>{
return (event)=>{
this.setState({[dataType]:event.target.value})
}
}
//表单提交的回调
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/>
密码:<input onChange={this.saveFormData('password')} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))
</script>
</body>
</html>不用函数柯里化的实现
<!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-dom,用于支持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">
//创建组件
class Login extends React.Component{
//初始化状态
state = {
username:'', //用户名
password:'' //密码
}
//保存表单数据到状态中
saveFormData = (dataType,event)=>{
this.setState({[dataType]:event.target.value})
}
//表单提交的回调
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={event => this.saveFormData('username',event) } type="text" name="username"/>
密码:<input onChange={event => this.saveFormData('password',event) } type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))
</script>
</body>
</html>二、组件的生命周期
1.引出组件生命周期demo
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/react/18.2.0/umd/react.production.min.js"></script>
<script
src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/prop-types/15.8.1/prop-types.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
class OpacityExample extends React.Component {
state = {
opacity: 1
}
//组件被加载到页面上
componentDidMount() {
this.timer = setInterval(() => {
let { opacity } = this.state
opacity = opacity - 0.1
if (opacity <= 0) {
opacity = 1
}
this.setState({ opacity })
}, 200);
}
//组件将要卸载
componentWillUnmount() {
//清除定时器
clearInterval(this.timer)
}
deleteEverything = () => {
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('example'))
}
render() {
console.log('render')
return (
<div>
<h1 style={{ 'opacity': this.state.opacity }}>我是一个文案正在逐渐变淡</h1>
<button onClick={this.deleteEverything}>删除一切</button>
</div>
)
}
}
ReactDOM.render(<OpacityExample />, document.getElementById('example'));
</script>2.组件生命周期(旧)
在React的旧版本中,组件的生命周期分为三个主要阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。每个阶段都有对应的生命周期函数。
挂载阶段
constructor(props):在组件被挂载之前调用,用于初始化状态和绑定事件处理程序。componentWillMount():在挂载开始之前调用,适合在这里执行一些同步的操作。render():必需的方法,用于返回组件的UI结构。componentDidMount():组件挂载后立即调用,可以在这里进行一些依赖于DOM的操作,如网络请求、订阅等。
更新阶段
componentWillReceiveProps(nextProps):当组件接收到新的props时调用,在这里可以根据新的props更新state。shouldComponentUpdate(nextProps, nextState):决定组件是否需要重新渲染,返回true或false。componentWillUpdate(nextProps, nextState):在组件重新渲染之前调用,可以在这里执行一些准备更新的操作。render():与挂载阶段的render方法相同,用于返回组件的UI结构。componentDidUpdate(prevProps, prevState):组件更新后立即调用,可以在这里进行一些依赖于DOM的操作。
卸载阶段
componentWillUnmount():组件即将从DOM中卸载时调用,可以在这里进行清理操作,如取消订阅、清除定时器等。
demo演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/react/18.2.0/umd/react.production.min.js"></script>
<script
src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/prop-types/15.8.1/prop-types.min.js"></script>
</head>
<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 true
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('Count---componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate(){
console.log('Count---componentDidUpdate');
}
render(){
console.log('Count---render');
const {count} = this.state
return(
<div>
<h2>当前求和为:{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(<Count/>,document.getElementById('test'))
</script>
</body>
</html>3.组件生命周期(新)
在React的新版中,组件的生命周期依旧分为三个阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。但是一些生命周期函数发生了变化,特别是引入了一些新的静态方法和弃用了一些旧的方法。
挂载阶段
constructor(props):在组件被挂载之前调用,用于初始化状态和绑定事件处理程序。static getDerivedStateFromProps(nextProps, prevState):在调用render方法之前调用,并且在初次挂载及后续更新时都会被调用。它应该返回一个对象来更新状态,或者返回null表示不更新任何状态。render():必需的方法,用于返回组件的UI结构。componentDidMount():组件挂载后立即调用,可以在这里进行一些依赖于DOM的操作,如网络请求、订阅等。
更新阶段
static getDerivedStateFromProps(nextProps, prevState):与挂载阶段相同。shouldComponentUpdate(nextProps, nextState):决定组件是否需要重新渲染,返回true或false。render():与挂载阶段的render方法相同,用于返回组件的UI结构。getSnapshotBeforeUpdate(prevProps, prevState):在最近一次渲染输出(提交到DOM节点)之前调用。它使得组件能在发生更改之前从DOM中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给componentDidUpdate()。componentDidUpdate(prevProps, prevState, snapshot):组件更新后立即调用。此方法不会在初次渲染时调用。可以在这里进行一些依赖于DOM的操作。
卸载阶段
componentWillUnmount():组件即将从DOM中卸载时调用,可以在这里进行清理操作,如取消订阅、清除定时器等。
示例代码
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.someValue !== prevState.someValue) {
return {
someValue: nextProps.someValue,
};
}
return null;
}
componentDidMount() {
// 组件挂载后立即调用,可以在这里进行一些依赖于DOM的操作
this.fetchData();
}
shouldComponentUpdate(nextProps, nextState) {
// 决定组件是否需要重新渲染
return nextProps.someValue !== this.props.someValue || nextState.data !== this.state.data;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 在最近一次渲染输出之前调用
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 组件更新后立即调用
if (prevProps.someValue !== this.props.someValue) {
this.fetchData();
}
}
componentWillUnmount() {
// 组件即将从DOM中卸载时调用
// 在这里进行清理操作,如取消订阅、清除定时器等
}
fetchData() {
// 模拟数据获取
setTimeout(() => {
this.setState({ data: 'Fetched data' });
}, 1000);
}
render() {
return (
<div>
<h1>{this.state.data}</h1>
</div>
);
}
}
getSnapshotBeforeUpdate 案例演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4_getSnapShotBeforeUpdate的使用场景</title>
<style>
.list{
width: 200px;
height: 150px;
background-color: skyblue;
overflow: auto;
}
.news{
height: 30px;
}
</style>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/react/18.2.0/umd/react.production.min.js"></script>
<script
src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/prop-types/15.8.1/prop-types.min.js"></script>
<script type="text/babel">
class NewsList extends React.Component{
state = {newsArr:[]}
componentDidMount(){
setInterval(() => {
//获取原状态
const {newsArr} = this.state
//模拟一条新闻
const news = '新闻'+ (newsArr.length+1)
//更新状态
this.setState({newsArr:[news,...newsArr]})
}, 1000);
}
getSnapshotBeforeUpdate(){
return this.refs.list.scrollHeight
}
componentDidUpdate(preProps,preState,height){
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
render(){
return(
<div className="list" ref="list">
{
this.state.newsArr.map((n,index)=>{
return <div key={index} className="news">{n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewsList/>,document.getElementById('test'))
</script>
</body>
</html>