【React自学笔记06】React18函数式组件()-react
【React自学笔记06】React18函数式组件()
React18函数式组件
- 基于React18新特性
- 在原有的React基础上,补充函数式组件的知识点及用法
一、React事件
- 在React中事件需要通过元素的属性来设置
- 和原生JS不同,在React中事件的属性需要使用驼峰命名法:
onclick -> onClick
onchange -> onChange
-
属性值不能直接执行代码,而是需要一个回调函数,事件触发时执行:
onclick=”alert(123)”
onClick={()=>{alert(123)}}
onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用
- onclick=”alert(123)”
- onClick={()=>{alert(123)}}
- onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用
-
在React中,无法通过在clickHandler函数中return false取消默认行为
event.preventDefault(); // 取消默认行为
event.stopPropagation(); // 取消事件的冒泡
- event.preventDefault(); // 取消默认行为
- event.stopPropagation(); // 取消事件的冒泡
-
事件对象event
React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象
React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题
- React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象
- React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
- 由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题
二、Props
基础用法
在组件中如果将数据全部写死,将会导致组件无法动态设置,不具有使用价值
我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据
父组件中可以直接在子组件标签中设置属性,作为传递的参数值
- 在函数组件中,属性props就相当于是函数的参数,可以通过参数来访问
- 可以在函数子组件的形参中定义一个props,props指向的是一个对象
- 它包含了父组件中传递的所有参数
注意:props只读不能修改
//Logs.js
import LogItem from './LogItem/logItem.js'
const Logs = () => {
// 模拟一组从服务器中拿回来的数据
const logsData = [
{
id: '001',
date: new Date(2021, 1, 20, 18, 30),
desc: '学习九阳神功',
time: 30
},
{
id: '002',
date: new Date(2022, 2, 10, 12, 30),
desc: '学习降龙十八掌',
time: 20
}
];
return (
<div className='logs'>{
logsData.map((item) => {
// return <LogItem key={item.id} date={item.date} desc={item.desc} time={item.time} />
return <LogItem {...item} />
})
}
</div >
)
}
export default Logs
//LogItem.js
import MyDate from './MyDate/MyDate.js'
import './LogItem.css'
const LogItem = (props) => {
return (
<div className='item'>
<MyDate date={props.date}/>
<div className='content'>
<h2 className="desc">{props.desc}</h2>
<div className="time">{props.time}分钟</div>
</div>
</div>
)
}
export default LogItem
import './MyDate.css'
const MyDate = (props) => {
const month = props.date.toLocaleString('zh-CN', { month: 'long' })
const day = props.date.getDate()
return (
<div className='date'>
<div className='month'>{month}</div>
<div className='day'>{day}</div>
</div>
)
}
export default MyDate
传递标签体
怎么把标签体传给card:props.className
//Card.js
import './Card.css'
const Card=(props)=>{
return (
<div className={`${props.className} card`}>{props.children}</div>
)
}
export default Card
三、State
在React中,当组件渲染完毕后(先执行),再修改组件中的变量(后执行),不会使组件重新渲染;要使得组件可以受到变量的影响,必须在变量修改后对组件进行重新渲染;这里我们就需要一个特殊变量,当这个变量被修改时,组件会自动重新渲染。
state相当于一个变量,只是这个变量在React中进行了注册,React会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染,使得我们的修改可以在页面中呈现出来。
注意:props是传给别人用的,state是对当前组件自身的属性(state是可变的,只属于当前属性)
定义步骤:
- 在函数组件中,我们需要通过钩子函数,获取state;
- 使用钩子 useState() 来创建state;
-
导入包:import {useState} from “react”;
useState()需要一个值作为参数,这个值就是state的初始值
该函数会返回一个数组
数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
-
useState()需要一个值作为参数,这个值就是state的初始值
该函数会返回一个数组
数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
- 该函数会返回一个数组
- 数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
- 数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
import { useState } from "react";
import './counter.css'
const Counter = () => {
const [counter, setCounter] = useState(1);
const addHandler = () => {
setCounter(counter + 1)
}
const minusHandler = () => {
setCounter(counter - 1)
}
return (
<div className="counter">
<h2>{counter}</h2>
<button onClick={addHandler}>+</button>
<button onClick={minusHandler}>-</button>
</div>
)
}
export default Counter
总结:
state实际就是一个被React管理的变量;当我们通过setState()修改变量的值时,会触发组件的自动重新渲染;只有state值发生变化时,组件才会重新渲染
当state的值是一个对象时,修改时是使用新的对象去替换已有对象
- setUser({…user, name: ‘猪八戒’});
-
const newUser = Object.assign({}, user);
newUser.name = ‘猪八戒’;
setUser(newUser);
如果直接修改旧的state对象,由于对象还是那个对象,所以不会生效
- user.name = ‘猪八戒’;
- setUser(user);
当通过setState去修改一个state时,并不表示修改当前的state,counter的初始值并没有改变;它修改的是组件下一次渲染时state值
setState()会触发组件的重新渲染,它是异步的;调完setState组件并不是立刻调用render函数进行渲染,而是先挂载到队列中,直到代码所有都执行完成再执行队列
所以当调用setState()需要用旧state的值时,一定要注意,有可能出现计算错误的情况;为了避免这种情况,可以通过为setState()传递回调函数的形式来修改state值
const addHandler = () => {
setTimeout(() => {
setCounter((prevCounter)=>{
/*
* setState()中回调函数的返回值将会成为新的state值
* 回调函数执行时,React会将最新的state值作为参数传递
* */
return prevCounter + 1;
});
// setCounter(prevState => prevState + 1);
}, 1000);
};
只要用到之前的值就传回调
四、Ref
useRef()返回的就是一个普通的JS对象 {current:undefined},所以我们直接创建一个js对象,也可以代替useRef()
const h1Ref = {current:null};
const h1Ref = useRef(); // 创建一个容器
区别:
- 我们创建的对象,组件每次重新渲染都会创建一个新对象
- useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象
- 当需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()
获取原生的DOM对象步骤
-
1.创建一个存储DOM对象的容器
使用 useRef() 钩子函数
① React中的钩子函数只能用于函数组件或自定义钩子
② 钩子函数只能直接在函数组件中调用
-
使用 useRef() 钩子函数
① React中的钩子函数只能用于函数组件或自定义钩子
② 钩子函数只能直接在函数组件中调用
- ① React中的钩子函数只能用于函数组件或自定义钩子
- ② 钩子函数只能直接在函数组件中调用
-
2.将容器设置为想要获取DOM对象元素的ref属性
React会自动将当前元素的DOM对象,设置为容器current属性
- React会自动将当前元素的DOM对象,设置为容器current属性
-
代码:
< h1 id="header" ref={h1Ref}>我是标题{count} h1>
h1Ref.current.innerText = ‘嘻嘻!’;
五、受控/非受控组件
非受控组件:监听表单项的变化
- 创建三个变量,用来存储表单中的数据
-
创建一个响应函数,监听相关数据的变化
函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息
event.target 执行的是触发事件的对象(DOM对象)
- 函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息
- event.target 执行的是触发事件的对象(DOM对象)
-
在React中,通常表单不需要自行提交,而是要通过React提交
取消表单的默认行为
获取表单项中的数据
将数据拼装为一个对象
- 取消表单的默认行为
- 获取表单项中的数据
- 将数据拼装为一个对象
//LogsForm.js
import Card from "../UI/Card"
import './LogsForm.css'
const LogsForm = () => {
let inputDate = ''
let inputDesc = ''
let inputTime = 0
const dateChangeHandler = (e) => {
inputDate = e.target.value
}
const descChangeHandler = (e) => {
inputDesc = e.target.value
}
const timeChangeHandler = (e) => {
inputTime = e.target.value
}
const formSubmitHandler=(e)=>{
e.preventDefault()
const newLog={
date:new Date(),
desc: inputDesc,
time: +inputTime
}
}
return (
<Card className="logs-form">
<form onSubmit={formSubmitHandler}>
<div className="form-item">
<label htmlFor="date">日期</label>
<input id="date" type="date" onChange={dateChangeHandler} />
</div>
<div className="form-item">
<label htmlFor="desc">内容</label>
<input id="desc" type="text" onChange={descChangeHandler} />
</div>
<div className="form-item">
<label htmlFor="time">时长</label>
<input id="time" type="number" onChange={timeChangeHandler} />
</div>
<div className="form-btn">
<button >添加</button>
</div>
</form>
</Card>
)
}
export default LogsForm
受控组件(双向绑定)
将表单中的数据存储到state中,然后将state设置为表单项value值,当表单项发生变化,state会随之变化,反之,state发生变化,表单项也会跟着改变
state提升,提升到共用组件里
//app.js
import LogsForm from './Components/LogsForm/LogsForm'
import Logs from './Components/Logs/Logs'
import Counter from './Components/Counter/Counter.js'
import { useState } from 'react';
import './app.css'
const App = () => {
// 模拟一组从服务器中拿回来的数据
const [logsData, setLogsData] = useState([
{
id: '001',
date: new Date(2021, 1, 20, 18, 30),
desc: '学习九阳神功',
time: 30
},
{
id: '002',
date: new Date(2022, 2, 10, 12, 30),
desc: '学习降龙十八掌',
time: 20
}
])
const saveLogHandler = (newLog) => {
let id = Date.now() + ''
newLog = { id, ...newLog }
setLogsData([...logsData, newLog])
}
return (
<div className='app'>
<LogsForm onSaveLog={saveLogHandler} />
<Logs logsData={logsData} />
<Counter />
</div>
)
}
export default App
//Logs.js
props.onSaveLog(newLog)
React18函数式组件
- 基于React18新特性
- 在原有的React基础上,补充函数式组件的知识点及用法
一、React事件
- 在React中事件需要通过元素的属性来设置
- 和原生JS不同,在React中事件的属性需要使用驼峰命名法:
onclick -> onClick
onchange -> onChange
-
属性值不能直接执行代码,而是需要一个回调函数,事件触发时执行:
onclick=”alert(123)”
onClick={()=>{alert(123)}}
onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用
- onclick=”alert(123)”
- onClick={()=>{alert(123)}}
- onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用
-
在React中,无法通过在clickHandler函数中return false取消默认行为
event.preventDefault(); // 取消默认行为
event.stopPropagation(); // 取消事件的冒泡
- event.preventDefault(); // 取消默认行为
- event.stopPropagation(); // 取消事件的冒泡
-
事件对象event
React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象
React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题
- React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象
- React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
- 由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题
二、Props
基础用法
在组件中如果将数据全部写死,将会导致组件无法动态设置,不具有使用价值
我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据
父组件中可以直接在子组件标签中设置属性,作为传递的参数值
- 在函数组件中,属性props就相当于是函数的参数,可以通过参数来访问
- 可以在函数子组件的形参中定义一个props,props指向的是一个对象
- 它包含了父组件中传递的所有参数
注意:props只读不能修改
//Logs.js
import LogItem from './LogItem/logItem.js'
const Logs = () => {
// 模拟一组从服务器中拿回来的数据
const logsData = [
{
id: '001',
date: new Date(2021, 1, 20, 18, 30),
desc: '学习九阳神功',
time: 30
},
{
id: '002',
date: new Date(2022, 2, 10, 12, 30),
desc: '学习降龙十八掌',
time: 20
}
];
return (
<div className='logs'>{
logsData.map((item) => {
// return <LogItem key={item.id} date={item.date} desc={item.desc} time={item.time} />
return <LogItem {...item} />
})
}
</div >
)
}
export default Logs
//LogItem.js
import MyDate from './MyDate/MyDate.js'
import './LogItem.css'
const LogItem = (props) => {
return (
<div className='item'>
<MyDate date={props.date}/>
<div className='content'>
<h2 className="desc">{props.desc}</h2>
<div className="time">{props.time}分钟</div>
</div>
</div>
)
}
export default LogItem
import './MyDate.css'
const MyDate = (props) => {
const month = props.date.toLocaleString('zh-CN', { month: 'long' })
const day = props.date.getDate()
return (
<div className='date'>
<div className='month'>{month}</div>
<div className='day'>{day}</div>
</div>
)
}
export default MyDate
传递标签体
怎么把标签体传给card:props.className
//Card.js
import './Card.css'
const Card=(props)=>{
return (
<div className={`${props.className} card`}>{props.children}</div>
)
}
export default Card
三、State
在React中,当组件渲染完毕后(先执行),再修改组件中的变量(后执行),不会使组件重新渲染;要使得组件可以受到变量的影响,必须在变量修改后对组件进行重新渲染;这里我们就需要一个特殊变量,当这个变量被修改时,组件会自动重新渲染。
state相当于一个变量,只是这个变量在React中进行了注册,React会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染,使得我们的修改可以在页面中呈现出来。
注意:props是传给别人用的,state是对当前组件自身的属性(state是可变的,只属于当前属性)
定义步骤:
- 在函数组件中,我们需要通过钩子函数,获取state;
- 使用钩子 useState() 来创建state;
-
导入包:import {useState} from “react”;
useState()需要一个值作为参数,这个值就是state的初始值
该函数会返回一个数组
数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
-
useState()需要一个值作为参数,这个值就是state的初始值
该函数会返回一个数组
数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
- 该函数会返回一个数组
- 数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
- 数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
import { useState } from "react";
import './counter.css'
const Counter = () => {
const [counter, setCounter] = useState(1);
const addHandler = () => {
setCounter(counter + 1)
}
const minusHandler = () => {
setCounter(counter - 1)
}
return (
<div className="counter">
<h2>{counter}</h2>
<button onClick={addHandler}>+</button>
<button onClick={minusHandler}>-</button>
</div>
)
}
export default Counter
总结:
state实际就是一个被React管理的变量;当我们通过setState()修改变量的值时,会触发组件的自动重新渲染;只有state值发生变化时,组件才会重新渲染
当state的值是一个对象时,修改时是使用新的对象去替换已有对象
- setUser({…user, name: ‘猪八戒’});
-
const newUser = Object.assign({}, user);
newUser.name = ‘猪八戒’;
setUser(newUser);
如果直接修改旧的state对象,由于对象还是那个对象,所以不会生效
- user.name = ‘猪八戒’;
- setUser(user);
当通过setState去修改一个state时,并不表示修改当前的state,counter的初始值并没有改变;它修改的是组件下一次渲染时state值
setState()会触发组件的重新渲染,它是异步的;调完setState组件并不是立刻调用render函数进行渲染,而是先挂载到队列中,直到代码所有都执行完成再执行队列
所以当调用setState()需要用旧state的值时,一定要注意,有可能出现计算错误的情况;为了避免这种情况,可以通过为setState()传递回调函数的形式来修改state值
const addHandler = () => {
setTimeout(() => {
setCounter((prevCounter)=>{
/*
* setState()中回调函数的返回值将会成为新的state值
* 回调函数执行时,React会将最新的state值作为参数传递
* */
return prevCounter + 1;
});
// setCounter(prevState => prevState + 1);
}, 1000);
};
只要用到之前的值就传回调
四、Ref
useRef()返回的就是一个普通的JS对象 {current:undefined},所以我们直接创建一个js对象,也可以代替useRef()
const h1Ref = {current:null};
const h1Ref = useRef(); // 创建一个容器
区别:
- 我们创建的对象,组件每次重新渲染都会创建一个新对象
- useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象
- 当需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()
获取原生的DOM对象步骤
-
1.创建一个存储DOM对象的容器
使用 useRef() 钩子函数
① React中的钩子函数只能用于函数组件或自定义钩子
② 钩子函数只能直接在函数组件中调用
-
使用 useRef() 钩子函数
① React中的钩子函数只能用于函数组件或自定义钩子
② 钩子函数只能直接在函数组件中调用
- ① React中的钩子函数只能用于函数组件或自定义钩子
- ② 钩子函数只能直接在函数组件中调用
-
2.将容器设置为想要获取DOM对象元素的ref属性
React会自动将当前元素的DOM对象,设置为容器current属性
- React会自动将当前元素的DOM对象,设置为容器current属性
-
代码:
< h1 id="header" ref={h1Ref}>我是标题{count} h1>
h1Ref.current.innerText = ‘嘻嘻!’;
五、受控/非受控组件
非受控组件:监听表单项的变化
- 创建三个变量,用来存储表单中的数据
-
创建一个响应函数,监听相关数据的变化
函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息
event.target 执行的是触发事件的对象(DOM对象)
- 函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息
- event.target 执行的是触发事件的对象(DOM对象)
-
在React中,通常表单不需要自行提交,而是要通过React提交
取消表单的默认行为
获取表单项中的数据
将数据拼装为一个对象
- 取消表单的默认行为
- 获取表单项中的数据
- 将数据拼装为一个对象
//LogsForm.js
import Card from "../UI/Card"
import './LogsForm.css'
const LogsForm = () => {
let inputDate = ''
let inputDesc = ''
let inputTime = 0
const dateChangeHandler = (e) => {
inputDate = e.target.value
}
const descChangeHandler = (e) => {
inputDesc = e.target.value
}
const timeChangeHandler = (e) => {
inputTime = e.target.value
}
const formSubmitHandler=(e)=>{
e.preventDefault()
const newLog={
date:new Date(),
desc: inputDesc,
time: +inputTime
}
}
return (
<Card className="logs-form">
<form onSubmit={formSubmitHandler}>
<div className="form-item">
<label htmlFor="date">日期</label>
<input id="date" type="date" onChange={dateChangeHandler} />
</div>
<div className="form-item">
<label htmlFor="desc">内容</label>
<input id="desc" type="text" onChange={descChangeHandler} />
</div>
<div className="form-item">
<label htmlFor="time">时长</label>
<input id="time" type="number" onChange={timeChangeHandler} />
</div>
<div className="form-btn">
<button >添加</button>
</div>
</form>
</Card>
)
}
export default LogsForm
受控组件(双向绑定)
将表单中的数据存储到state中,然后将state设置为表单项value值,当表单项发生变化,state会随之变化,反之,state发生变化,表单项也会跟着改变
state提升,提升到共用组件里
//app.js
import LogsForm from './Components/LogsForm/LogsForm'
import Logs from './Components/Logs/Logs'
import Counter from './Components/Counter/Counter.js'
import { useState } from 'react';
import './app.css'
const App = () => {
// 模拟一组从服务器中拿回来的数据
const [logsData, setLogsData] = useState([
{
id: '001',
date: new Date(2021, 1, 20, 18, 30),
desc: '学习九阳神功',
time: 30
},
{
id: '002',
date: new Date(2022, 2, 10, 12, 30),
desc: '学习降龙十八掌',
time: 20
}
])
const saveLogHandler = (newLog) => {
let id = Date.now() + ''
newLog = { id, ...newLog }
setLogsData([...logsData, newLog])
}
return (
<div className='app'>
<LogsForm onSaveLog={saveLogHandler} />
<Logs logsData={logsData} />
<Counter />
</div>
)
}
export default App
//Logs.js
props.onSaveLog(newLog)