react
项目搭建
npx create-react-app 项目名称
npx 会自动去查找当前依赖包中胡可执行文件,如果找不到就会去环境变量中查找,找不到就会帮你安装。
如果想要 安装全局create-react-app :
npm i -g create-react-app //全局安装
create-react-app 项目名称 //创建
create-react-app创建的项目webpack等配置信息都是封装好的,为了灵活修改相关配置可以运行
npm run eject //暴露配制项
生命周期
只有class组件才会有生命周期,组件实例从被创建到被销毁的过程称为组件的生命周期。
挂载
constructor()
: 通过给this.state
赋值初始化的地方、为事件处理函数绑定事件。 在 React 组件挂载之前,会调用它的构造函数。getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。render()
: 是 class 组件中唯一必须实现的方法。componentDidMount()
: 在组件挂载后(插入 DOM 树中)立即调用。
更新
每当组件的 state 或 props 发生变化时,组件就会更新。
-
getDerivedStateFromProps()
: 在调用 render 方法之前调用。 -
shouldComponentUpdate()
:当 props 或 state 发生变化时,shouldComponentUpdate()
会在渲染执行之前被调用。 -
render()
-
getSnapshotBeforeUpdate()
: 在最近一次渲染输出(提交到 DOM 节点)之前调用。能在组件发生更改之前从DOM中捕获一些信息,比如滚动位置,此生命周期方法的任何返回值将作为参数传递给componentDidUpdate()
-
componentDidUpdate()
: 在更新后会被立即调用。卸载
-
componentWillUnmount()
: 在组件卸载及销毁之前直接调用。
组件传值
父组件 ->子组件
子组件通过props接受
import React, { Component } from 'react'
import Son from '../Son'
//父组件
export default class Father extends Component {
constructor() {
super();
this.state = {
age: 666,
title: '昨天,今天,明天'
}
}
render() {
return (
<div>
//两种写法
<Son {...this.state} value={this.state.age} />
</div>
)
}
}
import React, { Component } from 'react'
//子组件
export default class Son extends Component {
constructor(props) {
super();
this.state = { name: this.props.title}
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
<h1>{this.props.age}</h1>
<h1>{this.props.value}</h1>
</div>
)
}
}
子组件->父组件
子组件调用父组件的方法,子组件调用,将要传递的数据,作为回调函数的参数。
import React, { Component } from 'react'
import Son from '../Son'
//父组件
export default class Father extends Component {
constructor() {
super();
this.state = {
age: 666,
title: '昨天,今天,明天'
}
}
changeAge(data){
this.setState({age:666})
}
render() {
return (
<div>
<h1>{this.state.age}</h1>
<Son {this.state.age} changeAge={this.changeAge.bind(this)}/>
</div>
)
}
}
import React, { Component } from 'react'
//子组件
export default class Son extends Component {
constructor(props) {
super();
this.state = { name: this.props}
}
// 使用了箭头函数,使用时不需要重新绑定this
handleChange = (e) => {
this.props.changeAge(e.target.value)
}
render() {
return (
<div>
<input type="text" value={this.props.value} onChange={this.handleChange} />
</div>
)
}
}
兄->弟
将状态共享,提升到最近的公共父组件中,由父组件管理状态
import React, { Component } from 'react'
import Son from '../Son'
import Daughter from '../Daughter'
//父组件
export default class Father extends Component {
constructor() {
super();
this.state = {
age: 666,
}
}
addAge=()=> {
this.setState({ age: this.state.age - 1 })
}
render() {
return (
<div>
<Daughter value={this.state.age}/>
<Son addAge={this.addAge}/>
</div>
)
}
}
import React, { Component } from 'react'
//子组件Son
export default class Son extends Component {
render() {
return (
<div>
<button onClick={() => { this.props.addAge() }}>按钮</button>
</div>
)
}
}
import React, { Component } from 'react'
//子组件Daughter
export default class Daughter extends Component {
render() {
return (
<div>
<h2>{this.props.value}</h2>
</div>
)
}
}
设置全局eventbus事件总线进行通讯
npm add -D events
import { EventEmitter } from 'events'
//导入事件总线,利用这个对象发射和监听事件,这个对象是全局的
const eventBus = new EventEmitter()
export default eventBus
import React, { Component } from 'react'
import eventBus from './event'
//需要传递事件的组件
export default class Father extends Component {
sendEvent = () => {
let arr = [1, 23, 4]
let obj = { name: 'Y' }
//发送事件eventBus.emit('事件名',参数)
eventBus.emit('eventName', arr, obj)
}
render() {
return (
<div>
<button onClick={this.sendEvent}>按钮</button>
</div>
)
}
}
import React, { Component } from 'react'
import eventBus from './event'
//接受事件的组件
export default class Brother extends Component {
// 添加事件监听
componentDidMount() {
eventBus.addListener('eventName', this.event)
}
event(a, b) {
console.log(a, b)
}
render() {
return (
<div>Brother</div>
)
}
//移除监听事件
componentWillUnmount() {
eventBus.removeListener('eventName', this.event)
}
}
Context 跨组件传递数据
无需为每层组件手动添加props,就能在组件树间进行数据传递的方法,当前的context的值由上层组件中距离当前组件最近的.Provider 决定的。
import React from 'react'
const ThemeContext = React.createContext('90')
export default ThemeContext
import React, { Component } from 'react'
import Son from '../Son'
import ThemeContext from '../ContextApi'
//父组件
export default class Father extends Component {
render() {
return (
<div>
<ThemeContext.Provider value='数据'>
<Son />
</ThemeContext.Provider>
</div>
)
}
}
import React, { Component } from 'react'
import ThemeContext from '../ContextApi'
//孙子组件
export default class GrandSon extends Component {
static contextType = ThemeContext;
render() {
return (
<div>
<h1>{this.context}</h1>
</div>
)
}
}
推荐使用 受控组件 来处理表单数据,受控组件就是表单元素的value需要state来获取。非受控组件使用ref从Dom节点中获取表单数据。
<input type="text" value={this.state.value} onChange={(e) => { this.setState({ value: e.target.value })}} />
高阶组件
高阶组件就是一个函数,这个函数接收一个组件作为参数,并返回一个新的组件。
import React, { Component } from 'react'
function Warp(WarpComponent) {
class hightOrder extends Component {
state = {
num: 88
}
render() {
return (
<WarpComponent num={this.state.num} />
)
}
}
return hightOrder
}
export default Warp
import React, { Component } from 'react'
import hightOrder from '../hightOrder'
export class Father extends Component {
render() {
return (
<div>
<p>{this.props.num}</p>
</div>
)
}
}
export default hightOrder(Father)
组件优化
父组件中的state或者props发生更新的时,会触发子组件的更新,会导致没有必要的性能浪费。
可以使用shouldComponentUpdate(nextProps,nextState)
生命周期让子组件不更新 (不推荐)
可以使用PureComponent来代替 Component。PureComponent通过props和state的浅比较,代替了shouldComponentUpdate的工作,但是只能比较外层数据,只要外层相同,就认为没有变化。不适合多层嵌套对象。
可以使用memo包裹函数组件,函数组件给定相同props的情况下渲染相同的结果,包装在memo中调用,将跳过组件渲染的操作,复用最近一次渲染结果。与PureComponent相似,但只是适用于函数组件,不适用class组件。
Hooks
函数式组件没有状态生命周期,hooks可以让你不写class组件的情况下使用state以及其他的React特性。Hooks只能在函数组件中使用。
useState
//class组件
//设置变量初始值
this.state={name:'hello'}
//使用
{this.state.name}
//更新
this.setState({name:'hello world'})
//hooks
//引入useState
import React,{useState} from 'react'
//设置变量,方法
const [name,setName]=useState('hello')
//使用
{name}
//更新
setName('hello world')
useEffect
函数式组件中没有生命周期,可以使用useEffect替代,useEffect可以看做组件加载,更新,卸载的集合。
//只模拟componentDidMount
import React,{useState} from 'react'
useEffect(()=>{
console.log('挂载')
},[])
// 模拟componentDidUpdate和componentDidMount
useEffect(()=>{
console.log('挂载更新')
},[参数])
想检测哪个数据更新就把哪个数据写到数组里面,如果要检测所有变量的更新可以把所有变量都写到数组中、也可以删掉数组。
useEffect(()=>{
console.log('挂载更新')
})
//模拟componentWillUnmount
useEffect(()=>{
reurn ()=>{
console.log('卸载')
}
})
useRef
//class
import React,{createRef} from 'react'
inputRefs=createRef()
<input type="text" ref={this.inputRefs} />
//hooks
import React,{useRef} from 'react'
const inputRefs=useRef()
<input type="text" ref={inputRefs} />
createRef:每次渲染的时候都会创建一个ref对象
useRef:第一次渲染时创建一个对象后,之后重新渲染的时候。如果对象创建过就不在创建新的ref对象,useRef性能会更好一些。
useContext
//class --> Context 跨组件传递数据
//hooks
const themes={
background:'pink'
}
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes}>
<Toolbar />
</ThemeContext.Provider>
);
//取值
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background }}>按钮</button>
);
}
useReducer
//接收两个参数,reducer函数和参数初始值。
const [state,dispatch]=useReduer((state,action)=>{
switch(action){
case:'yes':
return '123'
default:
return '321'
}
},0)
//
<button onClick={()=>dispatch('yes')><button>
useMemo
会在渲染期间执行,仅会在某个依赖项改变时才重新计算,可以避免在每次渲染时都进行高开销计算。如果没有提供依赖项数组,useMemo在每次渲染时都会重新计算新的值。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback
useCallback(fn, deps)相当于useMemo(() => fn, deps)。
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useMem返回是函数的运行结果,useCallback返回为函数。
useImperativeHandle
可以让你使用ref时自定义暴露给父组件的实例。应该与forwardRef一起使用。只暴露父组件需要用到的Dom方法。
import React, { useRef, forwardRef, useImperativeHandle } from 'react'
const JMInput = forwardRef((props, ref) => {
const inputRef = useRef()
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus()
},
}))
return <input type="text" ref={inputRef} />
})
export default function ImperativeHandleDemo() {
const inputRef = useRef()
return (
<div>
<button onClick={() => inputRef.current.focus()}>聚焦</button>
<JMInput ref={inputRef} />
</div>
)
}
useLayoutEffect
与 useEffect
差不多,useLayoutEffect可以解决一些特性场景下页面闪烁问题。会阻塞渲染,尽量不要用。
function App() {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
if (count == 0) {
const randomNum = 10 + Math.random()*200
setCount(10 + Math.random()*200);
}
}, [count]);
return (
<div onClick={() => setCount(0)}>{count}</div>
);
}
useDebugValue
允许用户推迟屏幕更新优先级不高部分。
useDebugValue(date, date => date.toDateString());