生命周期 旧
初始化阶段
由React.render 触发 -- 初次渲染
- constructor() 构造函数
- componentWillMount() 组件挂前
- render() 渲染
- componentDidMount() 组件挂载后
更新阶段
由组件内部this.setState() 或 父组件render触发
- componentWillReceiveProps(nextProps) 当props发生变化时
- shouldComponentUpdate(nextProps,nextState) 是否更新, return true 更新 false不更新
- componentWillUpdate (nextProps,nextState) 组件更新前
- render() 渲染
- componentDidUpdate(prevProps,prevState) 组件内更新完毕
卸载组件
由ReactDOM.unmountComponentAtNode(要卸载的元素节点) 触发
-
componentWillUnmount() 卸载组件
生命周期 新
初始化阶段:
由ReactDOM.render()触发-----初次渲染
- constructor()
- getDerivedStateFromProps-------表明state完全取决于props
- render()
- componentDidMount()
更新阶段:
由组件内部this.setSate()或父组件重新render触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate-------在更新之前保留页面的快照信息
- componentDidUpdate()
卸载组件:
由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
新旧区别:
首先是给三个生命周期函数加上了 UNSAFE:
1. UNSAFE_componentWillMount
2. UNSAFE_componentWillReceiveProps
3. UNSAFE_componentWillUpdate
原来(React v16.0前)的生命周期在React v16推出Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。
禁止不能用比劝导开发者不要这样用的效果更好,所以除了shouldComponentUpdate,其他在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。
如果在v16.3后的版本使用三个被禁用的生命周期,在其前面需要加上UNSAFE__,而在v17版本有可能直接被干掉无法使用。
同时新增了两个生命周期函数:
- getDerivedStateFromProps
- getSnapshotBeforeUpdate
static getDerivedStateFromProps(props, state) 静态方法生命周期钩子
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
我们知道getDerivedStateFromProps是一个静态函数,在其中是无法访问到组件实例的,也就是强制开发者在render之前只做无副作用的操作,而是根据props和state决定新的state,仅此而已。
getSnapshotBeforeUpdate(prevProps, prevState) 保存状态快照
getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate(prevProps, prevState, snapshot)。
基础知识
JSX语法
JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 {开头),就用 JavaScript 规则解析
JSX 语法允许直接在模板插入 JavaScript 变量,如果这个变量是一个数组,则会展开这个数组的所有成员(数组成员可以为 HTML 元素)
React 组件
React 允许将代码封装成组件,然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass() 方法就用于生成一个组件类。
所有组件都必须有自己的 render 方法,用于输出组件。组件类的第一个字母必须大写。
组件的用法与原生的 HTML 标签完全一致,可以任意加入属性(组件属性用 this.props 对象获取)
组件属性 class 需要用 className 代替,for 用 htmlFor 代替,因为 class 和 for 为 JavaScript 保留字。
this.props.children
this.props 对象的属性与组件的属性一一对应,但是有一个例外,this.props.children 表示组件所有子节点。
相当于vue的 slot
function Dome() {
return (
<div>
{ this.props.children}
</div>
)
}
ReactDOM.renderr(<Dome> <div>Dome this.props.children的内容</div> </Dome>, document.getElementById('root'))
父子传参
父传子通过props传的, this.props.xxx接收
子传父 通过props传递方法 子调用该方法通过实参传递
条件渲染
React
中的条件渲染和 JavaScript
中的一致,使用 JavaScript
操作符 或 [条件运算符] && || (来创建表示当前状态的元素,然后让 React
根据它们来更新 UI
。
const Greeting = props => {
const isLoggedIn = props.isLoggedIn
if (isLoggedIn) {
return <UserGreeting />
}
return <GuestGreeting />
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
阻止组件渲染 在极少数情况下,你可能希望隐藏组件,即使它被其他组件渲染。让 render 方法返回 null 而不是它的渲染结果即可实现
列表 & Keys
const messages = ['hello', 'hi', 'how are you']
const List = props => {
const { messages } = props
const list = messages.map(t => <li key={t}>{t}</li>)
return <ul>{list}</ul>
}
表单
当用户提交表单时,HTML 的默认行为会使这个表单跳转到一个新页面。在 React 中亦是如此。
但大多数情况下,我们都会构造一个处理提交表单并可访问用户输入表单数据的函数。实现这一点的标准方法是使用一种称为受控组件的技术。
1、受控组件
<input> 或 <select> 都要绑定一个 change 事件,每当表单的状态发生变化,都会被写入组件的 state 中,这种组件在 React 中被称为受控组件。
2. 处理多个输入
你有处理多个受控的 input 元素时,你可以通过给每个元素添加一个 name 属性,来让处理函数根据 event.target.name 的值来选择做什么。
handleChange = event => {
const { value, name } = event.target
this.setState({
[name]: value
})
}
handleSubmit = e => {
console.log(`${this.state.username} ${this.state.email}`)
e.preventDefault()
}
render() {
return (
<div>
Username:
<input
name="username"
type="text"
value={this.state.username}
onChange={this.handleChange}
/>
<br />
Email:
<input
name="email"
type="text"
value={this.state.email}
onChange={this.handleChange}
/>
<br />
<button onClick={this.handleSubmit}>提交</button>
</div>
)
}
ref
用来访问dom对象
<input ref="input" />
取: this.refs.input
React Hooks
React的组件创建方式,一种是类组件,一种是纯函数组件
特点: 1. 纯函数组件没有状态
2. 纯函数组件没有生命周期
3. 纯函数组件没有this
4. 只能是纯函数
React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。而React Hooks 就是我们所说的“钩子”。
React Hooks的用法
一、userState():状态钩子
纯函数组件没有状态,useState()用于为函数组件引入状态。
下面我们使用Hooks重写上面的计数器
import React, {useState} from 'react'
const AddCount = () => {
const [ count, setCount ] = useState(0)
const addcount = () => {
let newCount = count
setCount(newCount+=1)
}
return (
<>
<p>{count}</p>
<button onClick={addcount}>count++</button>
</>
)
}
export default AddCount
useState这个函数接收的参数是我们的状态初始值(initial state),它返回了一个数组,这个数组的第[0]项是当前当前的状态值,第[1]项是可以改变状态值的方法函数,我们约定为set前缀加状态的变量名
二、useContext():共享状态钩子
该钩子的作用是,在组件之间共享状态。关于Context这里不再赘述,其作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数据。
下面是一个例子,现在假设有A组件和B组件需要共享一个状态
import React,{ useContext } from 'react'
const Ceshi = () => {
const AppContext = React.createContext({})
const A =() => {
const { name } = useContext(AppContext)
return (
<p>我是A组件的名字{name}<span>我是A的子组件{name}</span></p>
)
}
const B =() => {
const { name } = useContext(AppContext)
return (
<p>我是B组件的名字{name}</p>
)
}
return (
<AppContext.Provider value={{name: 'hook测试'}}>
<A/>
<B/>
</AppContext.Provider>
)
}
export default Ceshi
三、useReducer():Action钩子
如遇到状态管理,我们一般会用到Redux,而React本身是不提供状态管理的。而useReducer()为我们提供了状态管理。
import React,{useReducer} from 'react'
const AddCount = () => {
const reducer = (state, action) => {
if(action.type === ''add){
return {
...state,
count: state.count +1,
}
}else {
return state
}
}
const addcount = () => {
dispatch({
type: 'add'
})
}
const [state, dispatch] = useReducer(reducer, {count: 0})
return (
<>
<p>{state.count}</p>
<button onClick={addcount}>count++</button>
</>
)
}
export default AddCount
useEffect():副作用钩子
组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合
默认情况下,它在第一次渲染之后和每次更新之后都会执行。你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。
useEffect(() => {
// 你需要执行的代码
}, []);
useEffect 初始化被调用一次,数据更新会被调用, 传空数组表示只初始化调用一次, 当传[num, count] 表示:
当num或count值被改变,回调就会被调用, (数组里放的表示你要监听的值)