一、props和state的区别
- props是对外的接口(组件间传递数据),state是对内的接口(组件内传递数据)
2.state是私有的,可以认为是组件的“私有属性”,使用setState方法修改state
this.setState({
isOpen: !this.state.isOpen,
});
- 构建函数constructor是唯一可以初始化state的地方
constructor(props: Props) {
// 调用React.Component基础类的构建函数
super(props);
this.state = {
isOpen: false,
};
}
4.setState 是同步还是异步? 同步的
setState是同步执行,异步更新,react会优化真正修改时机,可能会合并多个修改后再渲染
tip:异步更新是一种结果的表现形式,是因为setState的值被缓存了。执行还是同步的
state处理发生在生命周期变化的时候
count : 0
1.
setState后count未变化
<button onClick={() => {
this.setState({count: this.state.count + 1})
console.log(this.state.count) // => 0
}}>{this.state.count}加一</button>
2.
setState第二参数:函数中可以获取改变后的值
<button onClick={() => {
this.setState({count: this.state.count + 1}, () => {
console.log(this.state.count) // => 1
})
}}>{this.state.count}加一</button>
3.
两次相同的setState 只会使用上一次操作的值来赋值到界面
<button onClick={() => {
this.setState({count: this.state.count + 1})
this.setState({count: this.state.count + 1})
// => 界面只会输出1
}}>{this.state.count}加一</button>
4.
解决连续setState:可以将setState的第一个参数改为函数,获取上一次值,改变这个值就会连续生效了
或者将两个setState放在setTimeout里
<button onClick={() => {
this.setState((preState, preProps) => {
return {count: preState.count + 1}
})
this.setState((preState, preProps) => {
return {count: preState.count + 1}
})
// => 界面输出2
}}>{this.state.count}加一</button>
// => 界面输出2
setTimeout(() => {
this.setState({count: this.state.count + 1})
this.setState({count: this.state.count + 1})
});
二、事件的处理
- 箭头函数
-
参数类型可以将鼠标放在onClick上看到
企业微信截图_1620294005701.png
这里必须是箭头函数,this才指向的是外部
handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.target 是点击元素
e.currentTarget 绑定事件的元素
对target类型作判断
if ((e.target as HTMLElement).nodeName === 'SPAN') {
this.setState({
isOpen: !this.state.isOpen,
});
}
};
render() {
return (
<div>
<button onClick={this.handleClick}>购物车</button>
</div>
);
}
}
- 使用bind达到实现箭头函数效果
constructor(props: Props) {
// 调用React.Component基础类的构建函数
super(props);
this.state = {
isOpen: false,
};
this.handleClick = this.handleClick.bind(this)
}
handleClick (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
this.setState({
isOpen: !this.state.isOpen,
});
};
三、hooks (钩子)
- 可以在非类组件中使用state,函数组件中需要处理副作用,用钩子把外部代码钩进来
- hooks 一律使用use作为前缀命名,常用钩子useState,useEffect,useContext(跨组件数据传递),useReducer(管理全局状态)useCallback(处理回调副作用),useRef(返回引用对象,不变)
- 有了hooks + 函数式组件, 不再需要类组件
类组件冗长且复杂,难以复用。想要复用只能用 无状态组件 或 HOC (高阶组件)
1. 状态钩子 useState()
这是react自带的hook函数,声明组件状态
返回值是一个只有两个元素的数组:[状态,状态更新函数(函数命名规则:set + 状态名称)]
// 这种形式就不需要在jsx中用this.state.count 取值了
import React, { useState } from "react"; // 引入useState
const App:React.FC = (props)=> {
const [count, setCount] = useState<number>(0)
return (
<div >
<button onClick={() => {
setCount(count + 1)
}}>{count}加一</button>
</div>
);
}
类组件中生命周期,state的改变,请求等操作对于函数(一个输入,只有一个返回值)来说都是副作用,
既然变为了函数式组件,就需要某种hooks来消除副作用
2. 副作用钩子 useEffect()
(1) 可以取代生命周期函数componentDidMount,componentDidUpdate和componentWillUnmount
(2)给函数式组件添加副作用(side effect)
const [robotGallery, setRobotGallery] = useState<any>([])
第二参数数组中写入变化的值,只有变化才触发第一参数的执行
useEffect(() => {
document.title = `${count}次`
}, [count])
如果第二参数是空数组,就是在模拟类组件的componentDidMount,只会在第一次渲染执行
如果没有第二参数,就是在模拟类组件的componentDidUpdate,那在每次ui改变后都会执行该副作用函数(谨慎)
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => setRobotGallery(data))
// 使用async await
// const fetchData = async () => {
// const res = await fetch('https://jsonplaceholder.typicode.com/users')
// const data = await res.json()
// setRobotGallery(data)
// }
// fetchData()
}, [])
- 使用上下文context (Provider,Consumer)跨组件传递 (vue有provide inject)
或者用hooks useContext接收数据
父组件
// 使用上下文关系状态context跨组件传递数据
const defaultValue = {
testName: 'jjj'
}
导出创造的上下文
export const nameContext = React.createContext(defaultValue)
ReactDOM.render(
<React.StrictMode>
使用Provider 包裹子组件,并传递数据defaultValue
<nameContext.Provider value={defaultValue} >
<App />
</nameContext.Provider>
</React.StrictMode>,
document.getElementById('root')
);
多级子组件 引入
1. 使用Consumer组件包裹组件内容。value就是传递来的数据
import {nameContext} from '../index'
const Robot: React.FC<RobotProps> = ({ id, name, email }) => {
return (
<nameContext.Consumer>
{(value) => {
return <li>
<p>{value.testName}</p>
</li>
}}
</nameContext.Consumer>
);
};
2. hooks :useContext 深度注入接受数据
引入useContext
import React, {useContext} from "react";
const Robot: React.FC<RobotProps> = ({ id, name, email }) => {
const value = useContext(nameContext)
return (
<li>
<p>{value.testName}</p>
</li>
);
};
5 。全局context
- 新建appState.tsx. 导出AppStateProvider组件
// appState.tsx
import React, {useState} from 'react'
// 使用上下文context跨组件传递数据
interface AppStateValue {
username: string,
shoppingCart: {items: {id: number, name: string}[]}
}
const defaultValue: AppStateValue = {
username: '123',
shoppingCart: { items: [] }
}
export const appContext = React.createContext(defaultValue)
//创建的context的初始值可以是undefined,
//类型定义React.Dispatch<React.SetStateAction<AppStateValue>>
//在 <appSetStateContext.Provider > 组件上可以找到
export const appSetStateContext = React.createContext<React.Dispatch<React.SetStateAction<AppStateValue>> | undefined>(undefined)
创建context组件Provider用来传递数据
export const AppStateProvider: React.FC = (props) => {
const [state, setState] = useState(defaultValue)
Provider组件只能用value来传递数据,
return <appContext.Provider value={state}>
// 需要修改shoppingCart,再创建一个context 用来传递setState方法。
// 两个Provider 嵌套
<appSetStateContext.Provider value={setState}>
{props.children}
</appSetStateContext.Provider>
</appContext.Provider>
}
- 在父组件引入AppStateProvider组件,包裹起子组件
import {AppStateProvider} from './AppState'
ReactDOM.render(
<React.StrictMode>
<AppStateProvider>
<App />
</AppStateProvider>
</React.StrictMode>,
document.getElementById('root')
);
- 在子组件中通过点击事件更新全局数据
import React, {useContext} from "react";
import {appContext, appSetStateContext} from '../AppState'
interface RobotProps {
id: number;
name: string;
email: string;
}
// 组件传递数据使用的是props,接口定义传入的参数类型
const Robot: React.FC<RobotProps> = ({ id, name, email }) => {
useContext获取不同的context中的数据
const value = useContext(appContext)
const setState = useContext(appSetStateContext)
点击事件,执行传过来的setState 方法,实现改变了全局的shoppingCart数据
const addToCart = () => {
if (setState) {
setState(state => {
return {
...state,
shoppingCart: {
items: [
...state.shoppingCart.items,
{id, name}
]
}
}
})
}
}
return (
<li>
<img alt="robot" src={`https://robohash.org/${id}`} />
<h2>{name}</h2>
<p>{email}</p>
<p>作者:{value.username}</p>
<button onClick={addToCart}>加入购物车</button>
</li>
);
};
网友评论