React React 是一个用于构建用户界面的 JavaScript 库。
原生js操作Dom繁琐、效率低
原生js直接操作dom,浏览器进行大量重绘重排
原生js没有组件化编码,代码复用率低
React的特点:
采用组件化模式、声明式编码,提高开发效率和组件复用率
在React Native中使用React语法进行移动端开发
使用虚拟dom+diff算法,减少与真实dom的交互
1.使用react(jsx) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <div id ="like_button_container" > </div > <script src ="https://unpkg.com/react@18/umd/react.development.js" crossorigin > </script > <script src ="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin > </script > <script src ="https://unpkg.com/babel-standalone@6/babel.min.js" > </script > <script type ="text/babel" > const vDom = ( <h1 > <span > hello react</span > </h1 > ) const domContainer = document .querySelector('#like_button_container' ); ReactDOM.createRoot(domContainer).render(vDom) </script >
虚拟dom是object类型的对象,比真实dom轻,最终会渲染成真实dom
1 console .log(typeof vDom, vDom instanceof Object )
2.jsx JSX 是 JavaScript XML 的简写,表示 JavaScript 代码中写 XML(HTML)格式的代码。一种在React组件内部构建标签的类XML语法。
语法规则: 定义虚拟dom时,不要写引号,使用小括号() 使用jsx表达式时,用{}包裹 样式的类名,使用className属性, style内联样式使用{{ }}
只有一个根标签 标签必须闭合 标签首字母:若小写字母开头,则转为html标签同名元素,如果没有对应同名元素则报错;若大写字母开头,就去渲染对应的组件,如果没有定义就报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const data = ['Angular' , 'React' , 'Vue' ]const listItems = data.map((item ) => <li key={item}>{item}</li> ) const vDom = ( <div> <h1>xxxxxx</h1> <ul> { listItems } </ul> </div> ) const domContainer = document .querySelector('#test' );ReactDOM.createRoot(domContainer).render(vDom)
state和事件处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Person { constructor (name, age ) { this .name = name this .age = age } speak ( ) { console .log(`my name is ${this .name} , age is ${this .age} ` ) } } class Student extends Person { constructor (name, age, sex ) { super (name, age) this .sex = sex } } const p1 = new Person('lucy' , 20 )p1.speak() console .log(p1) const s1 = new Student('xiaomi' , 22 , '男' )console .log(s1)s1.speak() class Weather extends React .Component { constructor (props ) { super (props) this .state = { isHot: false } this .handleClick = this .handleClick.bind(this ) } handleClick (e ) { this .setState(prevState => ({ isHot: !prevState.isHot })) } render ( ) { console .log(this ) return <h1 onClick ={this.handleClick} > today is {this.state.isHot ? 'hot': 'cold'}</h1 > } } ReactDOM.createRoot(document .getElementById('test' )).render(<Weather /> )
state不可直接更改,通过setState更改,合并更新; constructor构造器只执行一次,render调用1-n次;
通常,在 React 中,构造函数仅用于以下两种情况:
通过给 this.state 赋值对象来初始化内部 state。
为事件处理函数绑定实例
简写state和方法调用:
1 2 3 4 5 6 7 8 9 10 11 12 state = { isHot : false } render ( ) { console .log(this ) return <h1 onClick ={(e) => this.handleClick()}>today is {this.state.isHot ? 'hot': 'cold'}</h1 > } handleClick = (e ) => { this .setState(prevState => ({ isHot: !prevState.isHot })) }
props 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Woman extends React .Component { render ( ) { const { name, age } = this .props return ( <ul> <li>name: { name }</li> <li>age: { age }</li> </ul> ) } } const p = { name : 'lucy' , age : 20 }ReactDOM.createRoot(document .getElementById('test' )).render(<Woman {...p }/> )
约束props类型和必传,默认值: 引入<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Woman.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number } Woman.defaultProps = { name: 'cassie' , age: 99 } class Woman extends React .Component { static propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number } static defaultProps = { name: 'cassie' , age: 99 } }
函数式组件使用props:
1 2 3 4 function Welcome (props ) { return <h1 > Hello, {props.name}</h1 > ; } ReactDOM.createRoot(document .getElementById('test' )).render(<Welcome name ="lucy" /> )
注意:组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props.
refs string类型的refs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Demo extends React .Component { handleLeft (e ) { alert('input-left: ' + this .refs.left.value) } handleBlur (e ) { alert('input-right: ' + this .refs.right.value) } render ( ) { return ( <div> <input ref="left" id="left" type="text" placeholder="please input" /> <button onClick={(e ) => this .handleLeft(e)}>click me left</button> <input ref="right" id="right" onBlur={(e ) => this .handleBlur(e)} type="text" placeholder="please input" /> </div> ) } } ReactDOM.createRoot(document .getElementById('test' )).render(<Demo /> )
createRef创建的ref和回调ref:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Demo extends React .Component { constructor (props ) { super (props) this .leftRef = React.createRef() } handleLeft (e ) { console .log(this .leftRef); alert('input-left: ' + this .leftRef.current.value) } handleBlur (e ) { alert('input-right: ' + this .rightRef.value) } render ( ) { return ( <div> <input ref={this .leftRef} id="left" type="text" placeholder="please input" /> <button onClick={(e ) => this .handleLeft(e)}>click me left</button> <input ref={current => this .rightRef = current} id="right" onBlur={(e ) => this .handleBlur(e)} type="text" placeholder="please input" /> </div> ) } } ReactDOM.createRoot(document .getElementById('test' )).render(<Demo /> )
关于回调 refs 的说明:
如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。
通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
1 2 3 4 5 6 this .inputText = null this .rightRef1 = (current ) => { console .log('回调refs-class-绑定函数' , current); this .inputText = current } <input ref={this .rightRef1} id="right" onBlur={(e ) => this .handleBlur1(e)} type="text" placeholder="please input" />
事件处理 React事件的命名采用小驼峰式camelCase, 而不是纯小写。 使用jsx语法时,需要传入一个函数作为事件处理函数,而不是一个字符串。 不能通过return false的方式阻止默认行为,必须显式使用e.preventDefault(). 在js中,class的方法默认不会绑定this,可以使用onClick={() => this.handleClick()}或者handleClick = () => {}来绑定this 通过事件委托方式 向事件处理程序传递参数:
1 2 <button onClick={(e ) => this .deleteRow(id, e)}>Delete Row</button> / / 箭头函数 <button onClick={this.deleteRow.bind(this, id)}>Delete Row</ button>
受控组件:表单数据是由React组件来管理的(state) 非受控组件:表单数据由DOM节点来处理(ref)。
生命周期 挂载
当组件实例被创建并插入dom时,其生命周期调用顺序如下:
contructor()
static getDerivedStateFromProps()
render()
componentDidMount()
当组件的props或state发生变化时会触发更新。更新的生命周期调用顺序如下:
static getDerivedStateFromProps()
shouldComponentUpdate() 必须有返回值,返回值为假值,则不进行组件更新(强制更新除外)
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸载
当组件从 DOM 中移除时会调用如下方法:
生命周期图
新的生命周期比旧生命周期相比:废弃componentWillMount,componentWillUpdate,componentWillReceiveProps,新增getDerivedStateFromProps、getSnapshotBeforeUpdate
getDerivedStateFromProps:
会在调用 render 方法之前调用并且在初始挂载及后续更新时都会被调用。 它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
getSnapshotBeforeUpdate:
getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。 此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()。
配置代理 单个代理:
在package.json里配置proxy:
1 2 3 { "proxy" : "http://localhost:5000" }
在setupProxy配置多个代理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const { createProxyMiddleware } = require ('http-proxy-middleware' )module .exports = function (app ) { app.use( createProxyMiddleware('/api1' , { target: 'http://localhost:5000' , changeOrigin: true , pathRewrite: { '^/api1' : '' } }), createProxyMiddleware('/api2' , { target: 'http://localhost:5001' , changeOrigin: true , pathRewrite: { '^/api2' : '' } }) ) }
使用需要加前缀:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 getData = () => { axios.get('http://localhost:3000/api1/students' ).then(res => { console .log('success' , res) }, error => { console .log('error' , error) }) } getCarData = () => { axios.get('http://localhost:3000/api2/cars' ).then(res => { console .log('success' , res) }, error => { console .log('error' , error) }) }
解决多路径刷新页面样式丢失问题 1.public/index.html引入样式不写 ./ 写 / 2.public/index.html引入样式不写 ./ 写 %PUBLIC_URL% 3.使用HashRouter
Redirect的使用 一般卸载路由注册的最下方,当所有路由都无法匹配时,跳转到redirect所指定的路由
1 2 3 4 5 <Switch> <Route path="/home" component={Home} /> <Route path="/about" component={About} /> <Redirect to="/home" /> </Switch>
路由组件和一般组件 路由匹配:
1 2 <Route path="/about" component={About} /> <About />
存放位置:
一般组件:compoents 路由组件:pages
接收到props不同:
一般组件:组件标签传递什么就接收什么 路由组件:接收到3个固定属性history,location,match
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 history action: "PUSH" block: ƒ block(prompt) createHref: ƒ createHref(location) go: ƒ go(n) goBack: ƒ goBack() goForward: ƒ goForward() length: 32 listen: ƒ listen(listener) location: {pathname : '/home' , search : '' , hash : '' , state : undefined , key : 'x2v2h1' } push: ƒ push(path, state) replace: ƒ replace(path, state) location: hash: "" key: "x2v2h1" pathname: "/home" search: "" state: undefined match: isExact: true params: {} path: "/home" url: "/home"
嵌套路由 1.注册子路由要写父路由的path
2.路由的匹配是按照注册路由的顺序进行的
路由组件传递params参数(地址栏可见) 1.传递
1 <Link to={`/home/message/detail/${item.id} /${item.title} ` }>{item.message}</Link>
2.接收
1 <Route path="/home/message/detail/:id/:title" component={MessageDetail}></Route>
3.获取值
1 const { id, title } = this .props.match.params
路由组件传递search参数(地址栏可见) 1.传递
1 2 3 4 5 <Link to={`/home/message/detail/?id=${item.id} &title=${item.title} ` }>{item.message}</Link> <Link to={{ pathname: `/home/message/detail` , search: `?id=${item.id} &title=${item.title} ` }}>{item.message}</Link>
2.接收
1 <Route path="/home/message/detail" component={MessageDetail}></Route>
3.获取值
1 2 3 import qs from 'qs' const search = qs.parse(this .props.location.search.replace('?' , '' )) const { id, title } = search
路由组件传递state参数(地址栏不可见) 1.传递
1 2 3 4 <Link to={{ pathname: `/home/message/detail` , state: { id : item.id, title : item.title} }}>{item.message}</Link>
2.接收
1 <Route path="/home/message/detail" component={MessageDetail}></Route>
3.获取值
1 const { id, title } = this .props.location.state
参数地址栏不可见,刷新也能保留参数,但是清除浏览器历史记录后state为undefined.
编程式路由导航 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 this .props.history.push(`/home/message/detail/?id=${item.id} &title=${item.title} ` )this .props.history.push(`/home/message/detail/${item.id} /${item.title} ` )this .props.history.push(`/home/message/detail` , {id : 1 , title : 1 })this .props.history.replace(`/home/message/detail/?id=${item.id} &title=${item.title} ` )this .props.history.replace(`/home/message/detail/${item.id} /${item.title} ` )this .props.history.replace(`/home/message/detail` , {id : 1 , title : 1 })this .props.history.goForward()this .props.history.goBack()this .props.history.go(n)
withRouter 一般组件没有history,location, match这三个属性,要使用路由可以用withRouter 可以通过 withRouter 高阶组件访问 history 对象的属性和最近的 <Route>
的 match 。 当路由渲染时, withRouter 会将已经更新的 match , location 和 history 属性传递给被包裹的组件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import React, { Component } from 'react' import { withRouter } from "react-router-dom" class HeaderRouter extends Component { forward = () => { console .log('forward' ) this .props.history.goForward() } back = () => { console .log('back' , this .props.history) this .props.history.goBack() } render ( ) { return ( <div> <h1>HeaderRouter</h1> <button onClick={this .forward}>前进</button> <button onClick={this .back}>后退</button> </div> ) } } export default withRouter(HeaderRouter)
BrowserRouter 和 HashRouter 的区别 底层原理不一样:
BrowserRouter使用h5的history API,不兼容ie9及以下版本 HashRouter 使用的是URL的哈希值
url表现形式不一样:
BrowserRouter的路径使用/, 例如 “http://localhost:3000/home/news “ HashRouter的路径使用#, 例如 “http://localhost:3000#home/news “
刷新后对路由state参数的影响:
BrowserRouter:没有影响,state保存在history中 HashRouter:导致路由state参数的丢失
antd 使用 1.安装
1 2 npm install antd --save yarn add antd
2.引入 antd.js 和 antd.min.js 依赖 react、react-dom、dayjs,请确保提前引入这些文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import dayjs from 'dayjs' import 'dayjs/locale/zh-cn' import zhCN from 'antd/locale/zh_CN' import 'antd/dist/reset.css' import { ConfigProvider } from 'antd' dayjs.locale('zh-cn' ) render( <ConfigProvider locale={zhCN}> <App /> </ConfigProvider> )
使用antd-icon图标 1.安装
1 npm install --save @ant-design/icons
2.引入&使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { HomeOutlined, LoadingOutlined, SettingFilled, SmileOutlined, SyncOutlined, SearchOutlined } from '@ant-design/icons' <HomeOutlined style={{ fontSize : '20px' , color : 'red' }} /> <LoadingOutlined style={{ fontSize : '20px' , color : 'blue' }} /> <SettingFilled style={{ fontSize : '20px' , color : 'green' }} /> <SmileOutlined style={{ fontSize : '20px' , color : 'pink' }} /> <SyncOutlined style={{ fontSize : '20px' , color : 'yellow' }} /> <Button type="primary" icon={<SearchOutlined /> }>primary Button</Button>
redux Redux 是一个使用叫做 “action” 的事件来管理和更新应用状态的模式和工具库 它以集中式 Store(centralized store)的方式对整个应用中使用的状态进行集中管理,其规则确保状态只能以可预测的方式更新。 Redux 使用 “单向数据流
1个专门用于做状态管理的js库,不是react的插件库
它可以用在react、angular、vue等项目中,但基本与react配合使用
作用:集中式管理react应用中共享的状态
1.store:
当前 Redux 应用的 state 存在于一个名为 store 的对象中。 store 是通过传入一个 reducer 来创建的,并且有一个名为 getState 的方法,它返回当前状态值:
2.action:
action 是一个具有 type 字段的普通 JavaScript 对象。你可以将 action 视为描述应用程序中发生了什么的事件. action 对象可以有其他字段,其中包含有关发生的事情的附加信息。按照惯例,我们将该信息放在名为 payload 的字段中。 action creator 是一个创建并返回一个 action 对象的函数
3.reducer:
reducer 是一个函数,接收当前的 state 和一个 action 对象,必要时决定如何更新状态,并返回新状态。函数签名是:(state, action) => newState。 你可以将 reducer 视为一个事件监听器,它根据接收到的 action(事件)类型处理事件。
Dispatch:Redux store 有一个方法叫 dispatch。更新 state 的唯一方法是调用 store.dispatch() 并传入一个 action 对象。 store 将执行所有 reducer 函数并计算出更新后的 state,调用 getState() 可以获取新 state. Selector: Selector 函数可以从 store 状态树中提取指定的片段。随着应用变得越来越大,会遇到应用程序的不同部分需要读取相同的数据,selector 可以避免重复这样的读取逻辑
Redux 数据流: 早些时候,我们谈到了“单向数据流”,它描述了更新应用程序的以下步骤序列:
State 描述了应用程序在特定时间点的状况
基于 state 来渲染视图
当发生某些事情时(例如用户单击按钮),state 会根据发生的事情进行更新
基于新的 state 重新渲染视图
具体来说,对于 Redux,我们可以将这些步骤分解为更详细的内容:
初始启动:
使用最顶层的 root reducer 函数创建 Redux store
store 调用一次 root reducer,并将返回值保存为它的初始 state
当视图 首次渲染时,视图组件访问 Redux store 的当前 state,并使用该数据来决定要呈现的内容。同时监听 store 的更新,以便他们可以知道 state 是否已更改。
更新环节:
应用程序中发生了某些事情,例如用户单击按钮
dispatch 一个 action 到 Redux store,例如 dispatch({type: ‘counter/increment’})
store 用之前的 state 和当前的 action 再次运行 reducer 函数,并将返回值保存为新的 state
store 通知所有订阅过的视图,通知它们 store 发生更新
每个订阅过 store 数据的视图 组件都会检查它们需要的 state 部分是否被更新。
发现数据被更新的每个组件都强制使用新数据重新渲染,紧接着更新网页
Redux 使用 “单向数据流”:
State 描述了应用程序在某个时间点的状态,视图基于该 state 渲染
当应用程序中发生某些事情时:
视图 dispatch 一个 action
store 调用 reducer,随后根据发生的事情来更新 state
store 将 state 发生了变化的情况通知 UI
视图基于新 state 重新渲染
Redux 有这几种类型的代码
Action 是有 type 字段的纯对象,描述发生了什么
Reducer 是纯函数,基于先前的 state 和 action 来计算新的 state
每当 dispatch 一个 action 后,store 就会调用 root reducer.
(Redux原理图)https://img1.baidu.com/it/u=549036950,105530499&fm=253&fmt=auto&app=138&f=JPG?w=913&h=500 (Redux原理图)https://cn.redux.js.org/assets/images/ReduxDataFlowDiagram-49fa8c3968371d9ef6f2a1486bd40a26.gif
同步action: 一般对象类型{type: ‘’, data: null}为同步action 异步action:返回值为函数为异步action
redux-thunk 来开发特定的异步逻辑(异步action)
redux: js库 react-redux:react插件库
react-redux: 容器组件:负责和redux同学,将结果交给ui组件 ui组件:不能使用任何redux的api,只负责页面交互、呈现
如何创建容器组件,react-redux的connect函数: 容器组件会传给ui组件2个参数:mapStateToProps状态映射为props;映射操作状态的方法
1 connect(mapStateToProps, mapDispatchToProps)(CountUI)
容器中的store是靠props传进去,不是在容器直接引入
1 2 3 4 5 6 7 8 9 10 11 12 function mapStateToProps (state ) { return { count: state } } function mapDispatchToProps (dispatch ) { return { increment: (number ) => { dispatch(createIncrementAction(number)) }, decrement: (number ) => { dispatch(createDecrementAction(number)) }, incrementAsyc: (number, time ) => { dispatch(createIncrementActionAsync(number, time)) } } }
connect参数可简写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export default connect( state => ({count : state}), { increment: createIncrementAction, decrement: createDecrementAction, incrementAsyc: createIncrementActionAsync })(CountUI) import { Provider } from 'react-redux' import store from './store' <Provider store={store}> <App /> </Provider>
Redux的reducer一定要是纯函数 纯函数:只要函数的调用参数相同,就永远返回相同的结果。
实现纯函数:
不修改入参
不执行有副作用的操作(修改函数之外的其他变量、API调用等)
不调用其他非纯函数(Date.now()) 保证纯函数的原则,则每当我们dispatch一个相同的action,且初状态相同,则 总是能得到一个相同的结果(状态变化),因此可以实现redux的时间旅行功能。
1.安装redux-devtools插件
2.项目安装插件包
1 npm install --save redux-devtools-extension
3.项目使用
1 2 3 4 5 6 7 8 9 10 11 12 import { createStore, applyMiddleware, combineReducers } from 'redux' // 使用 Thunk Middleware 来做异步 Action import thunk from 'redux-thunk' import { composeWithDevTools } from 'redux-devtools-extension' // 此处引入 import countReducer from './reducers/count.js' import personReducer from './reducers/person.js' export default createStore(combineReducers({ count: countReducer, person: personReducer }), composeWithDevTools(applyMiddleware(thunk)))
打包本地运行 1 2 3 4 5 6 npm run build # 生成build文件夹 # static server: npm install -g serve serve -s build
setState setState更新状态的2种方法:
setState(nextState, callback?): nextState: 对象或函数。如果将一个对象作为nextState传递,它将被浅合并到this.state中。 callback: 可选的回调函数。如果指定,React将在提交更新后(状态更新界面也更新后)调用您提供的回调。
setState(updater, callback?): updater: 更新器函数。它必须是纯的,应该以state和props作为参数,并且应该返回要浅合并到this.state中的对象。React将把你的updater函数放在队列中,并重新呈现你的组件 callback: 可选的回调函数。如果指定,React将在提交更新后(状态更新界面也更新后)调用您提供的回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const { count } = this .statethis .setState({ count: count + 1 }, () => { console .log(this .state.count) }) this .setState((state, props ) => { console .log(state, props) return { count: state.count + 1 } })
总结: 对象式的setState是函数式的setState的简写方式,语法糖 使用规则:
如果新状态不依赖于原状态,使用对象式
如果新状态依赖于原状态,使用函数式
如果要在setState执行后获取最新的状态数据,要在callback回调函数中读取
路由懒加载(lazy & Suspense) 1 2 3 4 5 6 7 8 9 10 11 12 13 import React, { Component, lazy, Suspense } from 'react' const Home = lazy(() => import ('../pages/Home' ))const About = lazy(() => import ('../pages/About' ))<Suspense fallback={<h1 > Loading...</h1 > }> <Switch> <Route path="/home" component={Home} /> <Route path="/about" component={About} /> {} </Switch> </Suspense>
Hooks
react16.8版本增加的新特性新语法
可以让你在函数式组件中使用state以及其他react特性
常用hooks:
State hook: useState()
Effect hook: useEffect()
Ref hook: useRef()
useState():
1 2 3 4 5 6 7 8 9 10 11 12 13 const [state, setState] = useState(initalValue)const [age, setAge] = useState(28 );const [name, setName] = useState('Taylor' );const [todos, setTodos] = useState(() => createTodos());let [count, setCount] = useState(0 )function add ( ) { setCount(count => count + 1 ) }
useEffect():
useEffect(effect: React.EffectCallback, deps?: React.DependencyList | undefined): void
effect hook可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子) effect中的副作用操作:
发ajax请求
设置订阅/定时器
手动更改dom
语法:
1 2 3 4 5 6 7 useEffect(() => { return () => { } }, [depsList])
可以把useEffect hook看成以下三个函数的组合:
componentDitMount()
componentDidUpdate()
componentWillUnmount()
useRef():
useRef 是一个 React Hook,它能让你引用一个不需要渲染的值。改变 ref 不会触发重新渲染,所以 ref 不适合用于存储期望显示在屏幕上的信息。如有需要,使用 state 代替
用 ref 引用一个值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { useRef } from 'react' ;export default function Counter ( ) { let ref = useRef(0 ); function handleClick ( ) { ref.current = ref.current + 1 ; alert('You clicked ' + ref.current + ' times!' ); } return ( <button onClick={handleClick}> Click me! </button> ); }
不要在渲染期间写入 或者读取 ref.current
1 2 3 4 5 6 7 8 function MyComponent ( ) { myRef.current = 123 ; return <h1 > {myOtherRef.current}</h1 > ; }
事件处理程序或者 effects 中读取和写入 ref。
1 2 3 4 5 6 7 8 9 10 11 12 13 function MyComponent ( ) { useEffect(() => { myRef.current = 123 ; }); function handleClick ( ) { doSomething(myOtherRef.current); } }
通过 ref 操作 DOM:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { useRef } from 'react' ;function MyComponent ( ) { const inputRef = useRef(null ) function handleClick ( ) { inputRef.current.focus(); } return ( <> <input ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ) }
获取自定义组件的 ref:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import { forwardRef, useRef } from 'react' ;const MyInput = forwardRef((props, ref ) => { return <input {...props } ref ={ref} /> ; }); export default function Form ( ) { const inputRef = useRef(null ); function handleClick ( ) { inputRef.current.focus(); } return ( <> <MyInput ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); }
文档碎片Fragment 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <> <OneChild /> <AnotherChild /> </> <Fragment> <OneChild /> <AnotherChild /> </Fragment> <Fragment key={1 }> <OneChild /> <AnotherChild /> </Fragment>
createContext createContext 能让你创建一个 context 以便组件能够提供和读取。方便后代组件祖组件之间通信。
1.创建上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import React, { Component, createContext } from 'react' const UsernameContext = createContext()<UsernameContext.Provider value={{...this.state}}> <B /> </UsernameContext.Provider> class C extends Component { static contextType = UsernameContext render ( ) { return ( <div style={{margin : '20px 0 0 40px' }}> <h1>组件C:</h1> <h2>从B组件接收到的username: {this .context.username} age:{this .context.age}</h2> </div> ) } } function C ( ) { return ( <div style={{margin : '20px 0 0 40px' }}> <h1>组件C:</h1> <h2>从B组件接收到的username: <UsernameContext.Consumer> { value => `${value.username} age: ${value.age} ` } </UsernameContext.Consumer> </h2> </div> ) }
PureComponent PureComponent会在shouldComponentUpdate对props和state进行浅比较,跳过不必要的更新,提高组件性能。 但是props 和 state 不能使用同一个引用。
以下不回触发render:
1 2 3 const obj = this .stateobj.name = 'react' this .setState(obj)
render props向组件内部传入带内容的标签 vue中使用slot插槽 react中:render props 或者children props
render props:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 render ( ) { return ( <> <h1>app:</h1> <A render={(name ) => <B name ={name}/ > } /> </> ) } class A extends Component { state = { name: 'react' , } render ( ) { return ( <> <h1>A:</h1> {this .props.render(this .state.name)} </> ) } } class B extends Component { render ( ) { return <h1 > 我是B: {this.props.name}</h1 > } }
children props: 不能传递props
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 render ( ) { return ( <> <h1>app:</h1> <A> <B /> </A> </> ) } class A extends Component { state = { name: 'react' , } render ( ) { return ( <> <h1>A:</h1> {this .props.children} </> ) } }
错误边界 error boundary错误边界:用来捕获后代组件错误,渲染出备用页面
只能捕获后代组件生命周期 产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件 定时器中产生的错误
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 state = { error: '' } static getDerivedStateFromError (error ) { console .log(error) return { hasError: error } } componentDidCatch (error, info ) { console .log(error, info) }
组件通信方式 组件间关系:
父子组件 (props)
兄弟组件(非嵌套组件)(消息订阅-发布 集中式管理)
祖孙组件(跨级组件)(消息订阅-发布 集中式管理 context)
通信方式:
props: children props / render props
消息订阅-发布:pubs-sub
集中式管理:redux等
context: createContext、Provider、Consumer