React
为什么要使用react-dom?
经过后面的知识已经了解到有react-native的存在,我们知道RN属于原生应用的开发语言原生应用没有浏览器的存在,所以把react 的核心知识放在了react中让RN和web开发同时使用,而react-dom则是如何在react中进行dom渲染的核心内容,我们常使用react-dom中的render方法。
组件与元素
元素
html中原生的节点,跟原生的写法一样,小写。
组件
组件的第一个字母大写。
类组件的写法
函数式组件写法
存在缺点:没有生命周期钩子和状态。
jsx
jsx简介
一种语法糖通过webpack中的babel来编译jsx,内部通过js中的createElement()方法,来渲染一个dom节点
createElement('标签名',{'属性'},{'子节点'})
vscode中快速生成jsx基本代码块
-
vscode中如下插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mD4XZ3NN-1583310091040)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1580697917111.png)]
-
在jsx文件中输入rcc回车即可生成jsx基本代码块模板
css的引入
css-in-js
下载包:styled-components
会给元素添加一个唯一的类名
//footer.js
import styled from 'styled-components'
const FooterWrap = styled.div `
font-size: ${ props => props['font-size'] }
`
export {
FooterWrap
}
//Footer.jsx
import React from 'react'
import { FooterWrap } from './footer'
function Footer() {
return (
<FooterWrap font-size="200px">footer</FooterWrap>
)
}
export default Footer
styled-component中编写样式出现相应的提示
需要安装如下插件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2pYql0kz-1583310091042)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1580698112461.png)]
props
props.children
可以让组件中的子元素显示,与vue中的slot的功能一样,react中没有slot插槽的概念。
//List.jsx组件
<li>
{this.props.children}
</li>
//父组件
<List keyword={this.state.keyword}>
<div>hello</div>
</List>
defaultProps
当不传值 的时候设置一个默认值。
static defaultProps = {
keyword:'abc'
}
//写在外面
List.defaultProps={
keyword:'abc'
}
使用prop-types进行prop的类型的检查
使用:引入prop-types包
//具体的用法参考npm
//子组件中对父组件中传过来的数据进行验证
static propTypes={
//父组件中传过来的keyword的类型必须是string,并且不能为空
keyword:propTypes.string.isRequired
}
state
setState()
当state中的数据发生变化时,需要使用setState(),使用setState()函数就调用render()函数,注意这与Vue不同。
当状态中存储一个数组,为什么可以对数组进行push()而不会报错?
当state中存储的数据发生改变的时候,需要调用setState()进行修改,但是数组是一个地址的引用,而push数组只是改变它的元素,并没有改变它的地址,所以push不需要调用setStat(),但是也没有改变state,需要使用以下的方法:
this.setState({
//产生一个新的数组
arr:[...this.state.arr,e.target.value]
})
setState()是异步函数,要想拿到实时更新的数据,需要使用延时器,把他放大下一个事件循环之中,或者使用下面的写法来实现,vue中通过$ticknext()来实现
this.setState( {
keyword: value
}, () => {
console.log(this.state.keyword)
})
}
setState()引用原值的问题?
//使用以下的方法,其中state保证是最新的值,防止意外的修改
this.setState(state => {
return {
keyword: value,
arr: [
...state.arr,
value
]
}
})
render()
父组件的 render()函数调用,子组件的render()一定调用。
state
主要用于保存、修改、控制自己 的可变状态,state在组件的内部初始化,只能在组建的的内部进行访问和修改,你可以认为组件是一个局部的只能被自身控制的数据源,通过setstate()进行更新,会造成组件的重新的渲染。
pureComponent
定义组件使用pureComponent,可以使更新数据时如果数据没有发生改变就只进行虚拟dom树的比对而不重新渲染。
受控组件与非受控组件
React组件的数据渲染是否被调用者传递的props
完全控制,控制则为受控组件,否则非受控组件。
受控组件
组件中的属性值完全依赖父组件。
非受控组件
通过ref来实现,进行dom操作
//从react中解构出一个react对象
import React, { Component, createRef } from 'react'
class Form extends Component {
constructor() {
super()
//把对象的执行结果赋值给this.inputRef
this.inputRef = createRef()
}
state = {
// keyword: '',
arr: ['a', 'b']
}
handleKeyUp(e) {
if (e.keyCode === 13) {
let input = this.inputRef.current
this.props.onReceiveKeyword(input.value)
input.value = ''
// this.setState({
// keyword: ''
// })
}
}
render() {
// console.log(0)
return (
<>
<input
//设置一个ref属性
ref={this.inputRef}
onKeyUp={this.handleKeyUp.bind(this)}
type="text"
/>
</>
)
}
}
export default Form
事件处理
自定义事件
在constructor中定义bind(this)
直接在组件内定义一个非箭头函数的方法,然后在render里直接使用onClick={this.handleClick.bind(this)}(不推荐),因为根据bind的特性,每调用一次click都会创建一次handleClick函数,性能很低,应该直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)
在事件中传递参数
通过下面的方法传参。
handleClick=(value)=>{
console.log(value)
}
render(){
return (
<button onClick={()=>{
this.handleClick('abc')
}}></button>
)
}
handleClick=()=>{
return (value)=>{
console.log(value)
}
}
render(){
return (
<button onClick={
this.handleClick
}></button>
)
}
在事件中传递参数,使用科里化,可以让代码更加的优雅美观
getDerivedStateFromProps()钩子函数
触发这个钩子的四种场景。
constructor:创建的时候。
New props:创建一个新的属性时候。
setState():修改组件内的状态的时候。
foreceUpdate():执行强制更新函数的时候。
如果在父子组件的componentDidMount()钩子中同时做了相应的修改,那么子组件中的getDerivedStateFromProps()钩子是如何执行的?子组件中的render()函数有事如何执行的?
-
当在父子组件的componentMount()钩子中做相应的修改后,子组件中的这个钩先执行,父组件中的这个钩子后执行,但是他们中的代码几乎同时执行,所以此时getDERIVEDstateFromOProps()钩子和render函数合并为一个
-
如果需要他们不进行合并,可以将父组件的操作放入下一个事件循环当中(serTimeout())
与老钩子函数中的
Context(组件件信息传递)
与Vue中的provide和inject(提供器和注入器)相似。
//color_context.js
//创建一个context
import React,{createContext , Component} from 'react'
const colorContext = createContext()
//conext中的provider(提供器对象),和Consumer(注入器对象)
const {
Provider,
Consumer
}=colorContext
export {Provider,Consumer}
export default colorContext
//index.js
import ReactDom from 'react-dom'
import {Provider} from './pages/context/color_context'
import Parent from './pages/context/Parent'
ReactDom.render(
//提供器提供了属性value,其中存储了一个对象
<Provider value={{color:'red'}}>
<Parent></Parent>
</Provider>,
document.getElementById('root')
)
注入方式一:
import React , {Component} from 'react'
import colorContext from '../context/color_context'
import {Consumer} from '../context/color_context'
export default class Child1 extends Component{
//静态属性:挂载到当前的类上
//将colorContext注入到静态属性中,可以在context中提取提供器提供的数据
static contextType=colorContext
render(){
return (
<>
<div style={{color:this.context.color}}>child1</div>
</>
)
}
}
注入方拾二:
import React , {Component} from 'react'
import colorContext from '../context/color_context'
import {Consumer} from '../context/color_context'
export default class Child1 extends Component{
render(){
return (
// 提取器对象,获得提取器对象中的数据需要一个回调函数,其中的对象就是一个包括提供器中所有数据的对象
<Consumer>
{
(value)=>(<div style={{color:value.color}}>child1</div>)
}
</Consumer>
)
}
}
让context变成组件(增强能力)
//color_context.js
import React, { createContext, Component } from 'react'
const colorContext = createContext()
const {
Provider,
Consumer: MyConsumer
} = colorContext
//自定义一个提供器,将原生的提供器包裹在里面,增加一些其他的功能
class MyProvider extends Component {
constructor() {
super()
this.state = {
color: 'red'
}
}
changeColor = (color) => {
this.setState({
color
})
}
render() {
return (
<Provider
value={{
color: this.state.color,
changeColor: this.changeColor
}}
>
{this.props.children}
</Provider>
)
}
}
export {
MyProvider,
MyConsumer,
colorContext
}
//index.js
//用于解决,渲染的dom外只有唯一的包裹的元素,可以使用div,但不好 ,可以使用<></>
import React,{Fragment} from 'react'
import ReactDom from 'react-dom'
import {MyProvider} from './pages/context/color_context'
import Parent from './pages/context/Parent'
ReactDom.render{
//使用自定义的提供器
<MyProvider value={{color:'red'}}>
<Parent></Parent>
</MyProvider>,
document.getElementById('root')
)
//Parent.jsx
import React,{Component} from 'react'
// import Child1 from './Child1'
import {colorContext} from './color_context'
class Parent extends Component{
static contextType=colorContext
handleClick(green){
this.context.changeColor(green)
}
render(){
return (
<div style={{color:this.context.color}}>
parent
<button onClick={()=>{
this.handleClick('blue')
}}>change</button>
</div>
)
}
}
export default Parent
高阶组件
一个函数,可以把一个组件当做他的函数参数,这个函数就是高级组件。
//hoc.js
import React, { Component } from 'react'
export default (Comp) => {
return class extends Component {
render() {
return (
<Comp title="hello"></Comp>
)
}
}
}
/index.js
//用于解决,渲染的dom外只有唯一的包裹的元素,可以使用div,但不好 ,可以使用<></>
import React,{Fragment} from 'react'
import ReactDom from 'react-dom'
import {MyProvider} from './pages/context/color_context'
import Parent from './pages/hoc/Hoc'
ReactDom.render{
//使用自定义的提供器
<MyProvider value={{color:'red'}}>
<Hoc></Hoc>
</MyProvider>,
document.getElementById('root')
)
//Hoc.jsx
import React,{Component} crom 'react'
import hoc from './hoc'
class Hoc extends Component {
render(){
console.log(this.props)
return (
<div>
aaaaaa
</div>
)
}
}
//注意暴露一个高阶组件函数
export default hoc(Hoc)
渲染属性(RenderProps),增强组件
//RenderProps.jsx
import React, { Component } from 'react'
import RenderPropsChild from './RenderPropsChild'
export default class RenderProps extends Component {
render() {
return (
<div>
//父组件中定义一个函数属性,还可以在渲染函数中传入一个组件
<RenderPropsChild
render={
(item) => {
return (
<div style={{color: 'red'}}>{item}</div>
)
}
}
></RenderPropsChild>
</div>
)
}
}
//RenderPropsChild.jsx
import React, { Component } from 'react'
export default class RenderPropsChild extends Component {
state = {
item1: 'list item 1',
item2: 'list item 2'
}
render() {
return (
<div>
<h1>child</h1>
<div>
{
this.props.render(this.state.item1)
}
</div>
<div>
{
this.props.render(this.state.item2)
}
</div>
</div>
)
}
}
常用高阶组件
- memo()
- withRouter(),给组件添加路由信息
redux (React状态管理)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81EslQSt-1583310091044)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1575355532702.png)]
基本实现
//创建一个仓库
const {createStore}= require('redux')
const defaultState = {
counter:0,
}
//类似数组的reduce方法
const reducers = (state=defaultState,action) => {
switch(action.type){
case 'increment':
return {
//函数传递一个新的状态,原来状态不能改变,所以不能使用state.counter++
counter:state.counter+1
}
default:
return state
}
}
const store = createStore(reducers)
//实时监测状态的变化
store.subscribe(()=>{
console.log(store.getState())
})
store.dispatch(
{
type:'increment '
}
)
store.dispatch(
{
type:'increment'
}
)
redux实现异步
通过中间件来实现异步操作,在action-creaters和state之间添加一个中间件。
方法一:中间件使用thunk方法
//创建一个仓库
//redux中导入一个,应用中间件的方法
const {
createStore,
applyMiddleware
}= require('redux')
const defaultState = {
counter:0
}
//原生事件,需要default
const thunk=require('redux-thunk').default
//纯函数:给一个输入,给一个预期的输出
//类似数组的reduce方法
//给参数state设置一个默认值为defaultState
//reduces方法会自动执行一次
const reducers = (state=defaultState,action) => {
switch(action.type){
case 'increment':
//经过reducers函数返回一个新的状态,需要return
return {
//函数传递一个新的状态,原来状态不能改变,所以不能使用state.counter++
counter:state.counter+1
}
case 'loadData':
return {
counter:action.data
}
default:
return state
}
}
const store = createStore(reducers,applyMiddleware(thunk))
//实时监测状态的变化
store.subscribe(()=>{
console.log(store.getState())
})
// store.dispatch(
// {
// type:'increment'
// }
// )
//dispatch只能添加一个扁平的对象作为参数,由于加入了中间件所以可以是一个函数,被拦截进行处理,因此可以在这里进行异步请求
//此函数有一个参数,就是我们要使用的dispatch方法的引用
store.dispatch(dispatch=>{
setTimeout(()=>{
dispatch({
type:'loadData',
data:100
})
},3000)
})
thunk 内部实现过程
//thunk原码
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
module.exports = thunk;
//原码解析,相当于三层函数的嵌套,相当于以下代码
function createThunkMiddleware(extraArgument) {
//第一层函数,参数为为store对象,解构出dispatch和getState方法
return function({ dispatch, getState }) {
//第二层函数的参数next,相当于dispatch
return function(next) {
//第三层函数的参数所提交的action
return function(action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action)
}
}
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
module.exports = thunk;
利用thuck的模式编写redux异步处理的中间件
const myMiddleware = ({ dispatch, getState }) => {
return (next) => {
return (action) => {
//所提交的action
console.log(action)
}
}
}
module.exports = myMiddleware
方法二:使用中间件redux-saga
//app.js
const {
createStore,
applyMiddleware
} = require('redux')
const createSagaMiddleware = require('redux-saga').default
const sagaMiddleware = createSagaMiddleware()
const reducers = require('./reducer')
const store = createStore(reducers, applyMiddleware(sagaMiddleware))
const mySaga=require('./mySaga')
sagaMiddleware.run(mySaga)
store.subscribe(() => {
console.log(store.getState())
})
store.dispatch({
type: 'willloadData'
})
//mySaga.js
const {takeEvery,put}=require('redux-saga/effects')
module.exports = function * () {
yield takeEvery('willloadData', function * () {
let result = yield new Promise((resolve) => {
setTimeout(() => {
resolve(1000000)
},3000)
})
yield put({type:'loadData',data: result })
})
}
//reducers.js
const defaultState = {
counter: 0
}
// 纯函数
const reducers = (state=defaultState, action) => {
switch (action.type) {
case 'increment':
return {
counter: state.counter + 1
}
case 'loadData':
return {
counter: action.data
}
default:
return state
}
}
module.exports = reducers
状态的拆分(类似vue的模块化)
随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分,combineReducers函数把拆分的多个reduces合并成为一个,人后调用createStore,调用子reducerj将状态合并为一个。
const { combineReducers } = require('redux')
const counter = require('./reducers/counter')
const show = require('./reducers/show')
const reducers = combineReducers({
counter,
show
})
module.exports = reducers
合并后的状态
{
counter:{
}
show:{
}
}
redux在React中的使用
redux是独立于react的状态管理工具
引入react-redux工具
import React, { Component } from 'react'
//connect 链接store中的state和dispatch方法
//有两个参数,两个参数都是函数,第一个函数的形参是state,第二个参数的形参是dispatch
import { connect } from 'react-redux'
import store from '../redux/counter/store'
//import storeContext from '../context/store_context'
const mapStateToProps = state => ({
count: state.counter
})
const mapDispatchToProps = dispatch => ({
increment() {
dispatch({
type: 'increment'
})
}
})
class Counter extends Component {
increment = () => {
this.props.increment()
}
render() {
return (
<div>
{this.props.count}
<button onClick={this.increment}>+</button>
</div>
)
}
}
//connect函数的执行结果是一个高阶组件,传递一个组价作为参数
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
Router(路由)
引入包:react-router-dom
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i7PhD1fS-1583310091046)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1583225302246.png)]
import React,{Component} from 'react'
//引入其中的组件
import {
Route,
//Switch组件,实现路由匹配的排他性,渲染第一个匹配的组件
Switch,
//重定向,放在路由匹配的最下面,防止其他的匹配
Redirect,
//Nav的子类,组件类似Router-link,相比Nav可以添加ative-class
NavLink
} from 'react-router-dom'
import Movive from './Moive'
import Tv from './Tv'
export default class Index extends Component{
render(){
return(
<div>
<ul>
<li>
<NavLink to="/movie">电影</NavLink>
</li>
<li>
<NavLink to="/tv">电视</NavLink>
</li>
</ul>
<Switch>
<Route
path="/tv"
exact//也 可以实现精准匹配
component={Tv}
/>
<Route
path="/movie"
component={Movive}
/>
<Redirect
from="/"
to='/movie'
/>
</Switch>
</div>
)
}
}
//index.js
import React from 'react'
import ReactDom from 'react-dom'
import {BrowserRouter as Router} from 'react-router-dom'
import Index from './pages/router/Index'
ReactDom.render(
<Router>
<Index>
</Index>
</Router>,
document.getElementById('root')
)
//Movie.jsx
import React,{Component} from 'react'
export default class Movie extends Component {
render(){
return (
<div>
Movie
</div>
)
}
}
嵌套路由
在一个组件中,根据路由匹配渲染另一个组件,即实现了路由的嵌套。
import React,{Component} from 'react'
import {
Route,
Switch,
Redirect,
NavLink
} from 'react-router-dom'
import List from './list'
export default class Movie extends Component {
render(){
//props中的match是当前组件的路径
let {match}=this.props
console.log(match)
return (
<div>
Movie
<Route
path={match.path+'/list'}
component={List}
/>
</div>
)
}
}
路由传参
方式一:query的方式,但是根据他在props中的存储方式,使用起来不方便,所以不建议使用query进行传参。
方拾二:动态路由
import React,{Component} from 'react'
import {
Route,
Switch,
Redirect,
NavLink
} from 'react-router-dom'
import List from './list'
export default class Movie extends Component {
render(){
let {match}=this.props
console.log(this.props)
return (
<div>
//定义一个动态路由
<NavLink to="/moive/list/2">moive</NavLink>
<Route
path={match.path+'/list/:id'}
component={List}
/>
</div>
)
}
}
//在list组将的props中可以拿到动态路由
方式二的另一种写法
//在NavLink组件中to是一个对象,
to={{pathname:match.path+'list/2'}}
方式三:state属性传参
import React,{Component} from 'react'
import {
Route,
Switch,
Redirect,
NavLink
} from 'react-router-dom'
import List from './list'
export default class Movie extends Component {
render(){
let {match}=this.props
console.log(this.props)
return (
<div>
<NavLink to={{
pathname:match.path+'/list/2',
//可以存入props对象中
state:{id:2}
}}>moive</NavLink>
<Route
path={match.path+'/list/:id'}
component={List}
/>
</div>
)
}
}
//List组建的this.props.location.id中可以拿到传递的参数。
编程式路由
import React,{Component} from 'react'
import TvList from './tvList'
export default class Tv extends Component {
handleClick=()=>{
//使用props中history的push方法
this.props.history.push({pathname:'/moive',state:{id:3}})
}
render(){
return (
<div>
tv
<button onClick={this.handleClick}>moive</button>
</div>
)
}
}
路由中三种子组件的渲染方式
方式一:最常用最简单的方式component
component中传入的是组件对象的引用,而不是组件的实例。
方式二:render(基于高阶组件的渲染属性),传入一个函数,返回一个组件实例。
<Route
path="/tv"
render={(props)=>{
return (
//渲染组件实例,传入prop中的对象
<Tv {...props}/>
)
}}
/>
方式三:children (基于高级组件的渲染属性)
通过match获取路由信息
<Route
path="/tv"
children={(props)=>{
return (
<Tv {...props}/>
)
}}
/>
render和children的区别
children匹配所有的路径
404错误页面的定义
//注意404 页面路由所在的位置,需要放在最下面
import React,{Component} from 'react'
import {
Route,
Switch,
Redirect,
NavLink
} from 'react-router-dom'
import Movive from './Moive'
import Tv from './Tv'
import Page404 from './Page404'
export default class Index extends Component{
render(){
return(
<div>
<ul>
<li>
<NavLink to="/movie">电影</NavLink>
</li>
<li>
<NavLink to="/tv">电视</NavLink>
</li>
</ul>
<Switch>
<Route
path="/tv"
component={Tv}
/>
<Route
path="/movie"
component={Movive}
/>
<Redirect
//精准匹配
exact
from="/"
to='/movie'
/>
<Route
//或使用path=“*”
path=""
component={Page404}
/>
</Switch>
</div>
)
}
}
使用地图
百度地图开发平台
- 申请一个密钥,白名单
- 使用iframe(因为使用第三方地图必须 在一个页面中嵌入这个页面)
createref
ref.current:当前ref的引用。
Immutable.js
如何在Redux中使用Immutable.js学习资料总结
https://www.cnblogs.com/feiying100/p/7063138.html
https://segmentfault.com/a/1190000010676878?utm_source=tag-newest
immutable的特性
-
不可变对象
-
数据共享
seq
seq:一个对象没有map,可以使用seq进行map,是惰性的。
fromJS
把普通对象转化为immutable对象,由于需要遍历整个对象,所以消耗性能。
toJS
不需要解构,
get、set、getIn、setIn 、updateIn
set、get
setIn()、getIn()
深层的设置和获取,传入一个数组作为参数
updateIn()
参数传入一个回调,来进行更新。
range()
取一个范围存入数组。
take()
取几个
immutable与第三方包的结合
如果第三方包不是immutable形式的,
Hooks
https://github.com/happylindz/blog/issues/19
全部使用函数式组件,不在使用类组件。
Hooks的优点
- 代码的可读性和可维护性变高。
- 不使用高阶组件、渲染属性、装饰器,使组件的层级变浅
基本使用
export default props => {
//使用useState()钩子
//语法糖,底层还是与原来一样
//useState一个数组,可以解构,解构出的名字可以任意起
let [count,setCount] = useState(0)
let handleClcik= () => {
setCount(count+1)
}
return(
<div>{count}<button onClick={handleClick}>+</button></div>
)
}
setCount()
- 修改状态需要依赖原值的时候,需要回调setCount(()=> {})
- 不依赖原值,不需要回调
useEffect(参数一:调用后的操作,参数二:那些状态的改变调用此钩子)
- 相当于componentDidMount()
- 当不传第二个参数的时候,只要有状态发生变化,就调用;如果传入一个空数组,而其中有依赖的状态,会报错。
- 有一个return,组件卸载时调用
useContext
实现react中的上下文,即Vue中提供器和注入器。
知识补充
context可以设置默认值,只有不设置provider的时候才会生效,其他时候不生效。
出现的原因?
在函数式的组建中,注入器只能使用Consumer的而不能使用另一种方式,但是Consumer注入的方式支持16.8高版本的react,所以如果项目使用了Hooks,我们知道Hooks需要使用函数式组件,那么引入useContext是很有必要的。
基本使用
const colorContext = React.createContext("gray");
function Bar() {
const color = useContext(colorContext);
return <div>{color}</div>;
}
function Foo() {
return <Bar />;
}
function App() {
return (
<colorContext.Provider value={"red"}>
<Foo />
</colorContext.Provider>
);
}
useReducer
实现一个redux
缺点:不支持中间件,不能进行异步操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8lQgbMr-1583310091047)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1576593996721.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p85gQxWS-1583310091048)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1576593953063.png)]
useCallback()
出现原因?
//函数式的组件中,没有this,每次使用这个组件,其中定义的变量会被重新赋值,也就是下面的handleClick函数会被重新声明,当此函数传入到子组件中的时候尽管函数的内容没变,但是声明函数存储的地址发生变化,重新渲染子组件
function App() {
const handleClick = () => {
console.log('Click happened');
}
return <SomeComponent onClick={handleClick}>Click Me</SomeComponent>;
}
可以进行记忆,防止多次调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2M8ss9au-1583310091049)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1576566301742.png)]
useMemo
可以用来编写一个缓存组件。
编写一个缓存组件的方法
方法一:使用react.memo(组件),高阶组件
方法二:如果项目使用了Hooks,可以使用useMemo()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9S2UGvTW-1583310091050)(C:\Users\yuyang\AppData\Roaming\Typora\typora-user-images\1576568304912.png)]
方法三:使用pureComponent,如果外部传入的props没有变化,就不进行渲染,实现缓存的效果(存在残缺点)。
方法四 :使用memoization,实现类似vue计算属性的功能。
引入memoize-one包
import memoize from 'memoize-one'
state={ filterText: "" };
//相当于Vue的计算属性,在 list 或者 filter 变化时,重新运行 filter:
filter = memoize(
(list,filterText) => list.fliter(item => item.text.include(filterText))
)
render(){
// 计算最新的过滤后的 list。
// 如果和上次 render 参数一样,`memoize-one` 会重复使用上一次的值。
const filteredList=this.filter(this.props.list, this.state.filterText);
<>
<input onChange={this.handleChange} value={this.state.filterText}>
<ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
</>
}
useRef
函数式组件的Caputer value
函数式组件,中不能难道ref,需要forwradRef(),c此时可以拿到refs.current,但是为undefind,因为没有实现透传,需要使用useImperativeHandle()这个钩子。
自定义Hooks
需要以use开头。
componentWilReceiveProps
protal
React项目
搭建项目的开发环境
- 用cra(create-react-app)脚手架:yarn add create-react-app 项目名称
- 用customiz e-cra、react-app-rewired进行脚手架的增强来进行webpack的二次配置
- 使用react-dom:react-dom则是React剥离出的涉及DOM操作的部分
项目开发的两种方式
- 以功能进行划分
- 以角色进行划分
问题一:如果产生一个新页面的路由问题?
把所有的新页面放到一个Router中,作为顶级路由。
问题二:Hooks中使用(useReducer),进行状态管理,不是全局的?
useReducer产生的store,只在本组件能够进行修改使用,其他组件会获得初始值,不能获得最新值,
在普通的类组件中没有进行使用,不知道结果如何。
问题三:高阶组件实现的1px边框
通过高阶组件实现的1px边框,只能运用到高阶组件的参数组件上,组件内部的元素不能使用。
问题四:在componentDidmount进行异步请求并且使用setState对state进行修改,报错的问题
Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
出现原因?
发出异步的请求后数据还没回来,马上切换到其他的组件,此组件立即卸载,当数据请求回来后由于组件已经卸载造成错误。
解决方法
如果使用的是类组件,在componentWillunmount(),组件卸载前的钩子,使用方法终止异步请求;
如果使用了Hooks,在useEffect(),进行return一个回调函数,useEffect()中的回调函数在组件卸载的时候执行。