react hooks
eject
1. eject是讲react-script集合起来,方便了我们运行单页面程序,但如果我们要编译多页面,就需要修改react-script ,但它已经被封装起来不方便改动,所以有了eject,使用eject可以将react-script拆开到项目根目录方便我们修改,react-script拆开后多了webpack的配置文件和一些js代码.
2. eject不能逆向执行,而且没有重复命令
Context(class 组件)
Context在react可以跨组件传递变量,比如从爷爷组件到孙子组件传递变量,尽量少使用context,它会破坏组件的独立性
用法api
-
createContext
import React, { Component, createContext } from "react"; import "./App.css"; const BatteryContext = createContext(); class Middle extends React.Component { render() { return <Leaf />; } } class Leaf extends Component { render() { return ( <BatteryContext.Consumer> {(battery) => <h1>{battery}</h1>} </BatteryContext.Consumer> ); } } class App extends React.Component { state = { battery: 60, }; render() { return ( <div className="App"> <BatteryContext.Provider value={this.state.battery}> <button onClick={() => { this.setState({ battery: this.state.battery - 1 }); }} > Press </button> <Middle></Middle> </BatteryContext.Provider> </div> ); } } export default App;
由于BatteryContext.Consumer 有点繁琐,因此可以用this.context改进,用法如下
import React, { Component, createContext } from "react"; import "./App.css"; const BatteryContext = createContext(); class Middle extends React.Component { render() { return <Leaf />; } } class Leaf extends Component { static contextType = BatteryContext;//被修改的部分就在这个class中 render() { const battery = this.context; return <h1>{battery}</h1>; } } class App extends React.Component { state = { battery: 60, online: false, }; render() { const { online } = this.state; return ( <div className="App"> <BatteryContext.Provider value={this.state.battery}> <button onClick={() => { this.setState({ battery: this.state.battery - 1 }); }} > Press </button> <Middle></Middle> </BatteryContext.Provider> </div> ); } } export default App;
lazy
mpa是多页面,pwa渐进式应用;spa单页面应用
lazy是将指定组件的导入行为封装成react组件
页面上的图片只有到了图片的位置才去加载,这样可以节省流量
Webpack - Code Splitting 提供了这样一种能力,把一个页面的所有js模块人为划分为多个,分时导入,这样就需要用import
import 有两个应用场景,
- import * from ' ' 引用静态资源(最主要的使用场景)
- import还有另一个场景,动态导入模块, import(‘./detail.js’).then(…),webpack遇到这个会自动的拆分代码,一般情况下页面不会主动加载,只有使用到了的时候页面才回去加载它
- app.jsx
import React, { Component, lazy,Suspense } from "react";
import "./App.css";
// Code Splitting可以支持自定义命名 像这样 /* webpackChunkName:"about"*/ 生成的名字为about.chunk.js
const About = lazy(() => import(/* webpackChunkName:"about"*/"./About")); //lazy返回一个组件
//ErrorBoundary 错误边界
// componentDidCatch在错误边界的应用
class App extends Component {
state={
hasError:false
}
// componentDidCatch(){//任何组件加载错误时候自动调用
// this.setState({
// hasError:true
// })
// }
static getDerivedStateFromError(){//效果和上面类似,出错的时候会用此函数返回的state来setState,写法更加优雅而已
return {
hasError:true
}
}
render() {
//fallback中传入一个组件或者节点
if(this.state.hasError){//当lazy组件不能加载时就会显示下面的组件
return <div>出错了</div>
}
return (
<div>
<Suspense fallback={<div>loading</div>}>
<About></About>
</Suspense>
</div>
);
}
}
export default App;
About.jsx
import React, { Component } from 'react';
class About extends Component {
state = { }
render() {
return ( <div>about</div> );
}
}
export default About;
memo
原理是和class组件一样
解决react运行的效率问题
避免重新渲染的几种方式
-
import React, { Component,PureComponent} from "react"; class Foo extends Component { shouldComponentUpdate(nextProps, nextState) { //避免不必要的重新渲染,只有props改变了才重新渲染 if(nextProps.name===this.props.name){ return false; }else{ return true } } render() { console.log('foo render') return ( null ); } } class App extends Component { state = {count:0} render() { const {count} = this.state; return ( <div> <button onClick={()=>{this.setState({count:count+1})}}>add</button> <Foo name="Mike"></Foo> </div> ); } } export default App;
-
PureComponent
import React, { Component, PureComponent } from "react"; class Foo extends PureComponent { //用了pureCompoent可以实现不必要的更新,只有props变化才会重新渲染,比如父组件更新count但是Foo不会更新 //有局限,这个是浅比较,如果传入props内部属性变化不会重新渲染 render() { console.log("foo render"); return <div>{this.props.person.age}</div>; } } class App extends Component { state = { count: 0, person: { age: 1 } }; render() { const { count,person } = this.state; return ( <div> <button onClick={() => { this.setState({ count: count + 1 }); person.age+=1; this.setState({ person }) }} > add </button> <Foo name="Mike" person={person}></Foo> <div>count:{count}</div> </div> ); } } export default App;
如果Foo传入匿名回调函数在父组件更新的时候Foo也会更新,因为匿名函数的地址可能不一样,如下所示
<Foo name="Mike" person={person} cb={()=>{}}></Foo>
如果传入的不是匿名函数而是已经定义好的函数就不会有这种情况,如下所示
<Foo name="Mike" person={person} cb={this.callback}></Foo>
memo原理就是上面this.callback的方式来保持组件不更新,不同的是memo用于函数组件中
memo用法
import React, { Component, memo } from "react";
//更改为function组件后的代码
const Foo = memo(function Foo(props) {
console.log("foo render");
return <div>{this.props.person.age}</div>;
})
class App extends Component {
state = { count: 0, person: { age: 1 } };
callback(){}
render() {
const { count,person } = this.state;
return (
<div>
<button
onClick={() => {
this.setState({ count: count + 1 });
person.age+=1;
this.setState({
person
})
}}
>
add
</button>
<Foo name="Mike" person={person} cb={this.callback}></Foo>
<div>count:{count}</div>
</div>
);
}
}
export default App;
用法几乎一样,只是把
class Foo extends PureComponent {
//用了pureCompoent可以实现不必要的更新,只有props变化才会重新渲染,比如父组件更新count但是Foo不会更新
//有局限,这个是浅比较,如果传入props内部属性变化不会重新渲染
render() {
console.log("foo render");
return <div>{this.props.person.age}</div>;
}
}
变成了
const Foo = memo(function Foo(props) {
console.log("foo render");
return <div>{this.props.person.age}</div>;
})
hooks
![image-20200413160337724](react hooks.assets/image-20200413160337724.png)
![image-20200413160452789](react hooks.assets/image-20200413160452789.png)
![image-20200413160510881](react hooks.assets/image-20200413160510881.png)
![image-20200413160525574](react hooks.assets/image-20200413160525574.png)
![image-20200413160736757](react hooks.assets/image-20200413160736757.png)
副作用在这里不是坏的,是指除了渲染之外的操作,比如数据持久化,http请求,绑定解绑事件
useState
每次渲染要执行相同次数的useState 和class的state差不多,当传入函数时只在组件第一次渲染执行
const [count, setCount] = useState(() => {
console.log("initial count");
return 0;
});
const [size, setSize] = useState({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
});
useEffect
相当于一个ComponentWillMount和ComponentWillUnmount
useEffect(() => {
console.log(size)
window.addEventListener("resize", onResize, false);
return () => {
window.removeEventListener("resize", onResize, false);
};
},[]);
useEffect的两个特例,第二个参数不传是每次渲染都会更新,传空数组就只更新一次.
完整代码
import React from "react";
import { useState, useEffect } from "react";
// class App extends Component {
// state = { count:0 }
// render() {
// const {count} = this.state;
// return ( <button onClick={()=>{this.setState({count:count+1})}}>
// count:{count}
// </button> );
// }
// }
function App() {
const [count, setCount] = useState(() => {
console.log("initial count");
return 0;
});
const [size, setSize] = useState({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
});
const onResize = () => {
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
});
};
useEffect(() => {
console.log('count change')
document.title = count;
});
useEffect(() => {
console.log(size)
window.addEventListener("resize", onResize, false);
return () => {
window.removeEventListener("resize", onResize, false);
};
},[]);
return (
<button
onClick={(a) => {
setCount(count + 1);
}}
>
Click:({count})
width:({size.width})xheight:({size.height})
</button>
);
}
export default App;
Context Hooks(function组件) 和useContext
类组件的两种context
- consumer
- contextType
function组件有一种,useContext()
对比:
- Consumer可以多层嵌套,可以使用到多个context的值,但是使用不优雅
- contextType只有一个 所以一个类组件只能拿到一个context值,使用比较方便
- 函数组件的useContext可以获取多个context的值,使用方便优雅
例子:
import React from "react";
import { useState, createContext,Component,useContext } from "react";
const countContext = createContext();
class Foo extends Component {
render() {
//Consumer可以多层嵌套,可以使用到多个context的值,但是使用不优雅
return (
<countContext.Consumer>
{count=><h1>{count}</h1>}
</countContext.Consumer>
);
}
}
class Bar extends Component {
static contextType = countContext;
//contextType只有一个 所以一个类组件只能拿到一个context值
render() {
const count = this.context;
return ( <h1>{count}</h1> );
}
}
function Counter(){
const count = useContext(countContext);
//函数组件可以获取多个context的值,使用方便优雅
return ( <h1>
{count}
</h1> )
}
function App() {
const [count, setCount] = useState(() => {
console.log("initial count");
return 0;
});
return (
<div>
<button
onClick={(a) => {
setCount(count + 1);
}}
>
Click:({count})
</button>
<countContext.Provider value={count}>
<Foo></Foo>
<Bar></Bar>
<Counter></Counter>
</countContext.Provider>
</div>
);
}
export default App;
Memo 和 Callback Hooks (useCallback)
useMemo是针对一个函数逻辑是否重复执行
memo是针对一个组件的渲染是否重复执行
useMemo和useEffect类似,第一个参数是要执行的逻辑函数,第二个参数是依赖的变量数组
useMemo会执行函数并返回值(如果有return值)
import React from "react";
import { useState,useMemo } from "react";
function Counter(props) {
//函数组件可以获取多个context的值,使用方便优雅
return <h1>{props.count}</h1>;
}
function App() {
const [count, setCount] = useState(() => {
console.log("initial count");
return 0;
});
const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
return count*2;
},[count===3])
//第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
return (
<div>
<button
onClick={(a) => {
setCount(count + 1);
}}
>
Click:({count}) double:({double})
</button>
<Counter count={count}></Counter>
</div>
);
}
export default App;
如果useMemo返回的是一个函数,那么等价于useCallback.作用是使函数组件重新渲染时内部的函数不发生变化
useMemo(()=>fn) === useCallback(fn)
import React from "react";
import { useState,useMemo,memo } from "react";
import { useCallback } from "react";
const Counter = memo(function Counter(props) {
console.log('Counter render')
//函数组件可以获取多个context的值,使用方便优雅
return <h1 onClick={props.onClick}>{props.count}</h1>;
})
function App() {
console.log('app render')
const [count, setCount] = useState(() => {
console.log("initial count");
return 0;
});
const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
return count*2;
},[count===3])
const onClick=useCallback(()=>{
console.log('click');
},[])
const half = useMemo(()=>{ //useMemo会执行函数返回count的两倍
return double/4;
},[double])
//第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
return (
<div>
<button
onClick={(a) => {
setCount(count + 1);
}}
>
Click:({count}) double:({double}) half:({half})
</button>
<Counter count={double} onClick={onClick}></Counter>
</div>
);
}
export default App;
Ref hooks
useRef其中的一个使用场景是调用子组件的函数
import React from "react";
import { useState,useMemo,memo,PureComponent } from "react";
import { useCallback,useRef } from "react";
// const Counter = memo(function Counter(props) {
// console.log('Counter render')
// //函数组件可以获取多个context的值,使用方便优雅
// return <h1 onClick={props.onClick}>{props.count}</h1>;
// })
class Counter extends PureComponent{
speak(){
console.log('now counter is '+this.props.count)
}
render(){
const {props} = this;
console.log('Counter render')
return <h1 onClick={props.onClick}>{props.count}</h1>;
}
}
function App() {
console.log('app render')
const [count, setCount] = useState(() => {
console.log("initial count");
return 0;
});
const CounterRef = useRef()
const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
return count*2;
},[count===3])
const onClick=useCallback(()=>{
console.log('click');
CounterRef.current.speak()
},[CounterRef])
const half = useMemo(()=>{ //useMemo会执行函数返回count的两倍
return double/4;
},[double])
//第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
return (
<div>
<button
onClick={(a) => {
setCount(count + 1);
}}
>
Click:({count}) double:({double}) half:({half})
</button>
<Counter ref={CounterRef} count={double} onClick={onClick}></Counter>
</div>
);
}
export default App;
useRef的另一个作用是保持一个常量不变,比如定时器的id,原本it会随着app的重新渲染会重新定义it,但是如果使用useRef他就能在app重新渲染时保持同一个句柄
import React from "react";
import { useState,useMemo,memo,PureComponent } from "react";
import { useCallback,useRef } from "react";
import { useEffect } from "react";
// const Counter = memo(function Counter(props) {
// console.log('Counter render')
// //函数组件可以获取多个context的值,使用方便优雅
// return <h1 onClick={props.onClick}>{props.count}</h1>;
// })
class Counter extends PureComponent{
speak(){
console.log('now counter is '+this.props.count)
}
render(){
const {props} = this;
console.log('Counter render')
return <h1 onClick={props.onClick}>{props.count}</h1>;
}
}
function App() {
console.log('app render')
let it = useRef();
const [count, setCount] = useState(() => {
console.log("initial count");
return 0;
});
const CounterRef = useRef()
const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
return count*2;
},[count===3])
const onClick=useCallback(()=>{
console.log('click');
CounterRef.current.speak()
},[CounterRef])
const half = useMemo(()=>{ //useMemo会执行函数返回count的两倍
return double/4;
},[double])
useEffect(()=>{
it.current = setInterval(()=>{setCount(count=>count+1)},1000)
},[])
useEffect(()=>{
if(count>10){
clearInterval(it.current)
}
})
//第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
return (
<div>
<button
onClick={(a) => {
setCount(count + 1);
}}
>
Click:({count}) double:({double}) half:({half})
</button>
<Counter ref={CounterRef} count={double} onClick={onClick}></Counter>
</div>
);
}
export default App;
自定义hooks
优点是可复用性强,比如下面的useSize,既可以在app中用,也可以在couter子组件中使用
import React from "react";
// eslint-disable-next-line
import { useState,useMemo,memo,PureComponent } from "react";
// eslint-disable-next-line
import { useCallback,useRef } from "react";
// eslint-disable-next-line
import { useEffect } from "react";
function useCount(defaultCount){
const [count, setCount] = useState(defaultCount);
const it = useRef()
useEffect(()=>{
it.current = setInterval(()=>{
setCount(count=>count+1)
},1000)
},[])
useEffect(()=>{
if(count>10){
clearInterval(it.current)
}
})
return [count,setCount]
}
function useCounter(count){
const size = useSize()
return <h1>{count}sizeWidth{size.width}</h1>;
}
function useSize(){
const [size,setSize] = useState({
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
})
const onResize = useCallback(()=>{
setSize({
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
})
},[])
useEffect(()=>{
window.addEventListener('resize',onResize,false)
return ()=>window.removeEventListener('resize',onResize)
},[])
return size;
}
function App() {
console.log('app render')
let [count,setCount]=useCount(0)
const Counter = useCounter(count)
const size = useSize();
//第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
return (
<div>
<button
onClick={(a) => {
setCount(count + 1);
}}
>
Click:({count})
</button>
{size.width}x{size.height}
{Counter}
</div>
);
}
export default App;
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!