Skip to content
Xiaoyu's Blog
Go back

React学习记录 Day02

😀
本文记录了作者在学习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)。每个阶段都有对应的生命周期函数。

挂载阶段

  1. constructor(props):在组件被挂载之前调用,用于初始化状态和绑定事件处理程序。
  2. componentWillMount():在挂载开始之前调用,适合在这里执行一些同步的操作。
  3. render():必需的方法,用于返回组件的UI结构。
  4. componentDidMount():组件挂载后立即调用,可以在这里进行一些依赖于DOM的操作,如网络请求、订阅等。

更新阶段

  1. componentWillReceiveProps(nextProps):当组件接收到新的props时调用,在这里可以根据新的props更新state。
  2. shouldComponentUpdate(nextProps, nextState):决定组件是否需要重新渲染,返回true或false。
  3. componentWillUpdate(nextProps, nextState):在组件重新渲染之前调用,可以在这里执行一些准备更新的操作。
  4. render():与挂载阶段的render方法相同,用于返回组件的UI结构。
  5. componentDidUpdate(prevProps, prevState):组件更新后立即调用,可以在这里进行一些依赖于DOM的操作。

卸载阶段

  1. 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)。但是一些生命周期函数发生了变化,特别是引入了一些新的静态方法和弃用了一些旧的方法。

挂载阶段

  1. constructor(props):在组件被挂载之前调用,用于初始化状态和绑定事件处理程序。
  2. static getDerivedStateFromProps(nextProps, prevState):在调用render方法之前调用,并且在初次挂载及后续更新时都会被调用。它应该返回一个对象来更新状态,或者返回null表示不更新任何状态。
  3. render():必需的方法,用于返回组件的UI结构。
  4. componentDidMount():组件挂载后立即调用,可以在这里进行一些依赖于DOM的操作,如网络请求、订阅等。

更新阶段

  1. static getDerivedStateFromProps(nextProps, prevState):与挂载阶段相同。
  2. shouldComponentUpdate(nextProps, nextState):决定组件是否需要重新渲染,返回true或false。
  3. render():与挂载阶段的render方法相同,用于返回组件的UI结构。
  4. getSnapshotBeforeUpdate(prevProps, prevState):在最近一次渲染输出(提交到DOM节点)之前调用。它使得组件能在发生更改之前从DOM中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给componentDidUpdate()。
  5. componentDidUpdate(prevProps, prevState, snapshot):组件更新后立即调用。此方法不会在初次渲染时调用。可以在这里进行一些依赖于DOM的操作。

卸载阶段

  1. 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>


Share this post:

Previous Post
React学习记录 Day01
Next Post
小小自走棋