JSX渲染数据
this.state = {
hobby:['aaa','bbb','ccc']
}
render(){
return(
<ul>
{this.state.hobby.map((item)=>{return(<li><span>{item}</span></li>)})}
</ul>
)
}
// 解构
constuctor(){
super()
this.state = {
name:'张三',
age:12
}
}
render(){
const {name,age} = this.state // 解构
return (
<div><h1>{name}</h1></div>
)
}
// 绑定样式属性className
<div className="style"></div>
// 绑定style,需在{}中传入一个对象
<div style={{color:'red',fontSize:'20px'}}></div>
react中this指向
- this指向:谁调用指向谁,箭头函数不绑定this,箭头函数的this指向父级 解决this的方案:
{/*1:构造函数绑定this*/}
constuctor(){
super()
this.handleChange = this.handleChange.bind(this)
}
<button onClick={this.handleChange()}></button>
handleChange(){}
{/*2:调用时绑定this*/}
constuctor(){
super()
}
<button onClick={this.handleChange.bind(this)}></button>
handleChange(){}
{/*3:箭头函数*/}
constuctor(){
super()
}
<button onClick={()=>this.handleChange()}></button>
handleChange(){}
{/*4:箭头函数赋值*/}
constuctor(){
super()
}
<button onClick={this.handleChange}></button>
handleChange = () =>{}
react修改数据
通过state定义的数据,必须通过setState修改数据,setState会触发render()
constuctor(){
super()
this.state = {
name:'aaa'
}
}
<h1>{this.state.name}</h1> // bbbb
<button onClick={this.handleChange}></button>
handleChange = () =>{
this.setState({
name:'bbbb'
})
}
类组件
import react,{Component} from 'react'
export default App extends Component{
render(){return(<div></div>)}
}
- 组件名称是大写字母开头
- 类组件必须继承React.Component
- 类组件必须实现render函数
- constructor是可选的,初始化数据,绑定this
- this.state是状态,维护组件中内部的数据
- render方法必须返回一个jsx元素
函数组件
import React from 'react'
function App(){return (<div></div>)}
export default App
- 没有生命周期
- 没有this
- 没有状态
组件的生命周期
- 挂载阶段:组件第一次在DOM树中被渲染的过程
- 更新阶段:组件状态肤色很难过变化,重新更新渲染的过程
- 卸载阶段:组件从DOM树中被移除的过程
组件传值,属性的校验
import PropTypes from 'prop-types'
class Father extends Component {
render(){
//传递一个对象
const car = {brand:'bmw',color:'white'}
return (
<div>
<Son money={100} wealth={['a','b','c']} {...car}/>
</div>
)
}
}
class Son extends Component {
constuctor(props){
super(props)
}
render(){
return (<div><h1>{this.props.money}<h1/></div>)
}
}
//子组件添加一个propTypes属性
Son.propTypes = {money:PropTypes.number.isRequired}
父传子
- 父组件提供要传递的数据
- 给子组件标签添加属性,属性值为我们要传递的数据
- 子组件通过props接收父组件中传递的数据
子传父
思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数
- 父组件提供一个函数(用于接收数据)
- 将这个函数作为属性的值,传递给子组件
- 子组件通过props调用父组件的函数
- 将子组件中的数据作为参数传递给父组件的函数
跨级组件传
- 组件层次在3层内,使用props一层层的传递
- context对象,context相当于一个全局变量,不管嵌套有多深,可以随意地使用
- redux
组件的优化
第一种:避免组件的重复渲染
// 第一种
比较新旧的props,决定是否重新渲染组件,不相等,会重新渲染
shouldComponentUpdate(nextProps,nextState){}
// 第二种
PureComponent
浅比较props和state,(shallowEqual)实现的shouldComponentUpdate,
不会进行深层次的比较,只比较外层数据结构,只要外层相同,则认为没有变化。
PureComponent存在的问题:
- 函数式组件不可以用
- 页面进行浅比较,可能由于深层数据的不一致导致而产生错误判断,导致页面的不更新
- 不适合在多层次的嵌套对象
key
不建议使用index作为key的值
setState
setState是同步还是异步?
react18之前:
- 1:在合成事件中是异步的,在生命周期钩子中是异步的
- 2:react17中,在原生事件、定时器中是同步的 合成事件:react内部的一套事件处理机制,不是原生事件
同步更新:每更新state就执行一次render 全部更新完毕以后执行一次render(异步)
react18,实现自动批量更新,react将多次state更新进行合并处理,最终只进行一次渲染。[ReactDOM.createRoot]
react17,用reactDOM.unstable_batchedUpdates()方法实现手动批量更新[ReactDOM.render]
拷贝数据
ref 用于访问在render方法中创建的React元素,获取DOM,进行操作
- 用在组件上,就是组件实例
- 用在标签元素,获取的就是DOM元素
受控组件/非受控组件
受控组件、非受控组件指的都是表单元素:<input/><textarea/><select/>
受控组件,表单元素受react组件管理控制,表单的状态(数据)修改只能通过setState()更新
特点:数据可控,完全由react中的state来管理
非受控组件:使用Ref从DOM节点获取表单数据,表单数据交由DOM节点处理
特点:ref获取DOM节点进行数据更新
高阶组件
高阶函数:一个函数可以接收另外一个函数作为参数
- 接收一个或多个函数作为输入
- 返回一个函数
高阶组件是一个函数,参数是一个组件,返回一个组件
高阶组件通过props把参数传递给目标组件,目标组件就直接从props里拿数据
高阶组件是react中用于复用组件的逻辑的一种技巧,HOC不是react api的一部分,是一种基于react的组合特性而形成的设计模式
高阶组件就是参数是组件,返回值是一个组件,高阶组件就是一个函数
高阶组件解决的是功能的复用,不是代码的复用
高阶组件的主要功能是封装并分离组件的通用逻辑,让通用的逻辑在组件间更好的复用
hooks
// 函数式编程
import react from 'react'
const App = ()=>{return <div></div>}
export default App
函数式编程: 纯函数:就是一个函数,一个函数的返回结果只依赖自己的参数,并且执行的过程中没有副作用 副作用:除了干自己的主要任务外,还做了其他事情 当一个函数调用时,除了返回函数值以外,还对主调用函数产生附加的影响 组件尽量写成纯函数,如果需要外部的数据或者是功能,准备一钩子,把涉及到的数据或者是功能的代码钩进纯函数组件里
useState:声明状态变量
返回一个数组 第一个元素是当前的状态 第二个元素是一个函数,这个函数的作用是设置修改状态
// 声明一个state的变量,赋值初始化 0 只在首次渲染的时候使用,如果去更新的话,这个0 就会被忽略
// 可以多次调用,每次都是独立的
// 只可以在函数组件内使用
const [xxx,setXxx] = useState(0)
使用immer可以修改state
import produce from 'immer'
useEffect 可以为react组件完成副作用的操作
react中,一个纯函数式组件主要的功能是通过数据渲染UI,剩余的其他操作都是副作用 - ajax请求 - 手动DOM - 本地存储
// 每次都会执行
useEffect(()=>{])
// [] 依赖空数组,由于依赖为空,不会变化,只执行一次
useEffect(()=>{},[])
useEffect的第二个参数,是依赖的变量,是一个数组,可以有多个依赖项,一旦这一个依赖项发生变动,useEffect就会重新执行
依赖项执行机制:
1:默认状态:首次执行+每次组件更新都执行 2:添加一个空的[]:首次渲染时执行,执行一次 3:添加特定的依赖项:首次执行 + 依赖项发生更新时执行useEffect()函数
// ajax请求
const [list,setList] = useState([])
useEffect(()=>{
const initData = async()=>{
try{
const {data} = await axios.get('')
setList(data.data)
} catch(err){}
}
initData()
},[])
// 清除定时器
const[count,setCount] = useState(0)
useEffect(()=>{
const timer = setInterval(()=>{
setCount(count => count + 1)
})
// 清除定时器 return是effect可选的清除机制 每一个effect都可以返回一个清除函数
// return 是在组件被销毁之前才会执行
return () =>{
clearInterval(timer)
}
},[])
useContext
帮助我们跨组件通信
使用步骤: 1:使用createContext创建context对象 2:在顶层组件通过Provider提供数据 3:底层组件通过useContext(context)函数获取数据
import React,{useContext,createContext} from 'react'
const context = createContext()
function Grandson(){
const {count,setCount} = useContext(context)
return (
<div>
<span>{count}</sapn>
<button onClick={()=>setCount(count + 1)}>加1</button>
</div>
)
}
function Son(){
return (
<div><Grandson/></div>
)
}
function Father(){
const [count,setCount] = useState(0)
return (
<div>
<context.Provider value ={{count,setCount}}>
<Son/>
</context.Provider>
</div>
)
}
export default function App(){
return(
<div><Father/></div>
)
}
useRef
主要功能是帮助我们获取DOM元素或者组件,还尅保存值
1:引入useRef,调用useRef() 2:通过ref属性将useRef()的返回值绑定到元素上 3:useRef()返回的是一个对象,对象内有一个current属性
useReducer
useState提供组件状态的,useReducer是useState升级版,所有的useState规则,useReducer都适用
1:定义一个initState 2: 定义一个reducerh函数,把所有操作方法都放到这个函数里边 3:把initState跟reducer,通过的是useReducer关联起来,返回一个当前的state和dispatch 4:当需要计算时,使用dispatch传递一个action值,触发的reducer函数,返回一个新的state
useCallback
解决重复渲染问题,性能优化
useCallback起到一个缓存的作用,即便父组件重新渲染,useCallback包裹的函数不会重新生成,会返回上一次函数的引用
useMemo
父组件渲染,会生成一个新的对象,导致传递给子组件的person属性发生了变化,进而会导致子组件重新渲染,浅比较
第一个参数:是一个函数,返回的对象的指向同一个引用,不会创建新的对象 第二个参数是一个数组,只有数组的变量改变时,第一个参数的函数才会返回一个新的对象
高阶组件使用场景
高阶组件就是一个函数,这个函数接收一个组件作为参数,并且返回一个新的组件。 HOC的作用: 1:代码复用 2:增强props(不需要每一个组件都传递新增的属性,使用高阶组件传递可以给每一个组件新增props)
存在的缺陷:需要在原组件上包裹或者嵌套,如果存在大量的HOC,会影响性能
Portal
Portal提供了一种将子节点渲染到存在于父组件以外的DOM节点的方案
Portal可以让节点渲染到root节点以外的节点
悬浮框,提示框,对话框
prop.children
每一个组件都有props.children属性,通过这个可以方便的拿到内存元素,props.children表示组件的所有子节点
fragment
定义组件时,最外层的div不想被渲染时,可以使用