React基础知识(事件处理、受控组件与非受控组件、高阶函数、组件的生命周期)(三)
系列文章目录
第一章:React基础知识(React基本使用、JSX语法、React模块化与组件化)(一)
第二章:React基础知识(组件实例三大核心属性state、props、refs)(二)
第三章:React基础知识(事件处理、受控组件与非受控组件、高阶函数、组件的生命周期)(三)
文章目录
- 系列文章目录
- 一、事件处理
- 二、收集表单数据
- 2.1 非受控组件
- 2.2 受控组件
- 三、高阶函数
- 3.1 函数柯里化
- 3.2 不使用函数柯里化
- 四、组件的生命周期
- 4.1 react生命周期(17以前的旧版本)
- 4.2 react生命周期(17以后的新版本,主要看这个)
- 4.2.1. 即将废弃的勾子
为了简化代码,以下代码片段,除了有新的库需要引入会特意说明,其他引入react相关库的操作不再重复说明
一、事件处理
- 通过
onXxx
属性指定事件处理函数(注意大小写)- React使用的是自定义(合成)事件, 而不是使用的原生DOM————为了更好的兼容性
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————为了的高效
- 可以通过
event.target
得到发生事件的DOM元素对象(触发事件的对象就是你当前需要获取的对象) ————不要过度使用ref
代码片段:
<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 Demo extends React.Component {
myRef = React.createRef();
myRef2 = React.createRef()
showData = () => {
// console.log(this.myRef)
alert(this.myRef.current.value)
}
showData2 = (event) => {
alert(event.target.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button ref='btn' onClick={this.showData}>点我提示左侧数据</button>
<input type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} />
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'));
</script>
</body>
</html>
运行结果:
二、收集表单数据
2.1 非受控组件
要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以使用 ref 来从 DOM 节点中获取表单数据。在非受控组件中,你经常希望 React 能赋予组件一个初始值, 但是不去控制后续的更新。 在这种情况下,你可以指定一个 defaultValue 属性,而不是value。
简单的话说就是:仅仅获取用户输入得值,不绑定给state,现用现取
代码片段:
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库,代码看上方 -->
<script type="text/babel">
//创建组件
class Login extends React.Component{
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this
alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))
</script>
</body>
</html>
运行结果:
2.2 受控组件
在 HTML 中,表单元素(如 <input>
、 <textarea>
和 <select>
)通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
简单的话说就是:保存或者修改操作,绑定给state
代码片段:
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库,代码看上方 -->
<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>
运行结果:
三、高阶函数
高阶函数: 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
-
若A函数,
接收的参数是一个函数
,那么A就可以称之为高阶函数。 -
若A函数,调用的
返回值依然是一个函数
,那么A就可以称之为高阶函数。 -
常见的高阶函数有: Promise、setTimeout、arr.map()等等
函数的柯里化: 通过函数调用继续返回函数
的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){
return(b)=>{
return (c)=>{
return a+b+c
}
}
}
3.1 函数柯里化
代码片段:
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/babel">
//创建组件
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}>
//如果传递参数,此处会直接调用函数,onChange会被赋值成当前函数的返回值
用户名:<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>
运行结果:
3.2 不使用函数柯里化
代码片段:
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<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.组件从创建到死亡它会经历一些特定的阶段。
2.React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用
。
3.我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
4.1 react生命周期(17以前的旧版本)
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
-
constructor()
-
componentWillMount()
-
render()
-
componentDidMount() =====> 常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
- shouldComponentUpdate()
- componentWillUpdate()
- render() =====> 必须使用的一个
- componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
代码片段:
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<div id="test1"></div>
<!-- 引入react核心库 -->
<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()
}
//组件将要挂载的钩子
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>
<hr />
</div>
)
}
}
//父组件A
class A extends React.Component {
//初始化状态
state = { carName: '奔驰' }
changeCar = () => {
this.setState({ carName: '奥拓' })
}
//组件将要挂载的钩子
componentWillMount () {
console.log('A---componentWillMount');
}
//组件挂载完毕的钩子
componentDidMount () {
console.log('A---componentDidMount');
}
//控制组件更新的“阀门”
shouldComponentUpdate () {
console.log('A---shouldComponentUpdate');
return true
}
//组件将要更新的钩子
componentWillUpdate () {
console.log('A---componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate () {
console.log('A---componentDidUpdate');
}
render () {
console.log('A---render');
return (
<div>
<div>我是A组件</div>
<button onClick={this.changeCar}>换车</button>
{ /*组件嵌套,调用子组件内容*/}
<B carName={this.state.carName} />
</div>
)
}
}
//子组件B
class B extends React.Component {
//组件将要挂载的钩子
componentWillMount () {
console.log('B---componentWillMount');
}
//组件挂载完毕的钩子
componentDidMount () {
console.log('B---componentDidMount');
}
//组件将要接收新的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'))
ReactDOM.render(<A />, document.getElementById('test1'))
</script>
</body>
</html>
运行结果:
4.2 react生命周期(17以后的新版本,主要看这个)
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
-
constructor()
在 React 组件挂载之前,会调用它的构造函数。主要用于初始化state、为实例方法绑定this。如果组件类继承自React.Component,在构造函数中应先调用super(props),需要注意的是,不用在构造函数中使用this.setState()。还可以更改事件处理程序this的指向。
-
static getDerivedStateFromProps(props, state)
响应 Props 变化之后进行更新的方式。这个生命周期的功能实际上就是将传入的props映射到state上面。 该方法是一个静态方法,无法通过this直接访问。这个函数会在每次re-rendering之前被调用。该方法的返回值会修改state,如果不想修改state,则直接返回null
-
render()
该方法是 class 组件中唯一必须实现的方法。render函数应该是一个纯函数,意味着在不修改state值的时候该函数的返回值是一样的。
-
componentDidMount() =====> 常用
会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在componentWillUnmount() 里取消订阅 。
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
- static getDerivedStateFromProps(nextProps, nextState)
- shouldComponentUpdate(nextProps, nextState)
根据该方法返回值,返回值默认为true判断 React 组件的输出是否受当前 state 或 props 更改的影响,返回值默认为true。默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为
-
render()
-
getSnapshotBeforeUpdate(prevProps, prevState)
该钩子会在dom和refs渲染之前调用,返回的值可以传递给componentDidUpdate
-
componentDidUpdate(prevProps, prevState, snapshot)
会在更新后会被立即调用,首次渲染不会执行此方法。当组件更新后,可以在此处对DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。你也可以在componentDidUpdate() 中直接调用 setState() ,但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。它还会导致额 外的重新渲染,虽然用户不可⻅,但会影响组件性能。不要将 props “镜像”给 state,请考虑直接使用 props。
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() 当组件阶段当组件从 DOM 中移除时
代码片段:
<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 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 {...props}
return null
}
//在更新之前获取快照
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate');
return 'atguigu'
}
//组件挂载完毕的钩子
componentDidMount() {
console.log('Count---componentDidMount');
}
//组件将要卸载的钩子
componentWillUnmount() {
console.log('Count---componentWillUnmount');
}
//控制组件更新的“阀门”
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>当前求和为:{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>
运行结果:
4.2.1. 即将废弃的勾子
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。