系列文章目录
第一章:React基础知识(React基本使用、JSX语法、React模块化与组件化)(一) 第二章:React基础知识(组件实例三大核心属性state、props、refs)(二) 第三章:React基础知识(事件处理、受控组件与非受控组件、高阶函数、组件的生命周期)(三)
为了简化代码,以下代码片段,除了有新的库需要引入会特意说明,其他引入react相关库的操作不再重复说明
一、事件处理
- 通过
onXxx 属性指定事件处理函数(注意大小写)
- React使用的是自定义(合成)事件, 而不是使用的原生DOM————为了更好的兼容性
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————为了的高效
- 可以通过
event.target 得到发生事件的DOM元素对象(触发事件的对象就是你当前需要获取的对象) ————不要过度使用ref
代码片段:
<body>
<div id="test"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<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 = () => {
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>
<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>
<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>
<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}>
用户名:<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>
<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()触发---初次渲染
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
- shouldComponentUpdate()
- componentWillUpdate()
- render() =====> 必须使用的一个
- componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
代码片段:
<body>
<div id="test"></div>
<div id="test1"></div>
<script type="text/babel">
class Count extends React.Component {
constructor(props) {
console.log('Count---constructor');
super(props)
this.state = { count: 0 }
}
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>
)
}
}
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>
)
}
}
class B extends React.Component {
componentWillMount () {
console.log('B---componentWillMount');
}
componentDidMount () {
console.log('B---componentDidMount');
}
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()触发---初次渲染
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>
<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<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 }
}
add = () => {
const { count } = this.state
this.setState({ count: count + 1 })
}
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
force = () => {
this.forceUpdate()
}
static getDerivedStateFromProps(props, state) {
console.log('getDerivedStateFromProps', props, state);
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_前缀才能使用,以后可能会被彻底废弃,不建议使用。
|