开启学习react+ts,本篇主要是react函数式组件必备Hook,结合TS一起了解。
一、Hooks
1、useState
App.tsx 中使用 useState 定义数据,以及修改数据的方法,并传递给 Comp.tsx 子组件:
const [num, setNum] = useState(0);
<Comp1 num={num} />
子组件接收:
import React from 'react'
const Comp1: React.FC = function (props) {
return (
<>
<h3>{props.num}</h3>
<button>累加</button>
</>
)
}
export default Comp1;
很明显,这么接收直接就报错。因为TS强制要求必须指定传参的字段及其类型,因此应当改为:
import React from 'react'
const Comp1: React.FC = function (props: {num: number}) {
return (
<>
<h3>{props.num}</h3>
<button>累加</button>
</>
)
}
export default Comp1;
而实际上这是TS中接口的简化写法,完整点应该写为:
import React from 'react'
interface IProps {
num: number;
}
const Comp1: React.FC<IProps> = function (props) {
return (
<>
<h3>{props.num}</h3>
<button>累加</button>
</>
)
}
export default Comp1;
A. 事件直接父传子使用
目前 setNum 依然处于定义了但未使用的状态,因此ESlint又会一直给出提示,因此我们可以把这个累加的效果实现:
<Comp1 num={num} setNum={setNum} />
interface IProps {
num: number;
setNum: any
}
<button onClick={()=>props.setNum(props.num+1)}>累加</button>
注意:
这里虽然设置为any可以实现累加,但不建议这么操作。
因此,真正的做法:
import React from 'react'
interface IProps {
num: number;
setNum: (num:number)=>void;
}
const Comp1: React.FC<IProps> = function(props) {
return (
<>
<h3>{props.num}</h3>
<button onClick={()=>props.setNum(props.num+1)}>累加</button>
</>
)
}
export default Comp1;
B. 事件用子传父的做法
* 父组件
import React, {useState, useCallback} from 'react'
import Comp1 from 'components/Comp1'
const App: React.FC = () => {
const [num, setNum] = useState(0)
const toSetNum = (value: number) => setNum(value)
return (
<>
<h2>你好世界</h2>
<Comp1 num={num} toSetNum={()=>toSetNum} />
</>
)
}
export default App;
setNum(newValue):代表直接用新值替换初始值
setNum(preValue => newValue):代表用新值替换旧值
* 子组件
import React from 'react'
interface IProps {
num: number;
toSetNum: (num:number)=>void;
}
const Comp1: React.FC<IProps> = function(props) {
return (
<>
<h3>{props.num}</h3>
<button onClick={()=>props.toSetNum(props.num+1)}>累加</button>
</>
)
}
export default Comp1;
2、useEffect
React的Class Component中有 componentDidMount 、 componentDidUpdate 和 componentWillUnmount ,但Function Component并没有。
A、componentDidMount
useEffect(()=>{
console.log('componentDidMount')
}, [])
B、comopnentDidUpdate
useEffect(()=>{
console.log('comopnentDidUpdate')
}, [num])
如果监听路由的变化:
useEffect(()=>{
console.log('路由变化')
}, [location.pathname])
C、componentWillUnmount
useEffect(()=>{
return ()=>{
}
}, [])
3、memo、useMemo与useCallback
在Function Component中,也不再区分mount 和update 两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。useMemo 和useCallback 都是解决上述性能问题的。
来看下面这段代码:
import React, { useState, useMemo, useCallback } from "react";
const Sub = () => {
console.log("Sub被渲染了");
return <h3>Sub组件</h3>;
};
export default function App2() {
const [num, setNum] = useState<number>(0);
const changeNum = () => setNum(num + 1)
return (
<div>
<h2>num的值:{num}</h2>
<button onClick={changeNum}>累加num</button>
<Sub />
</div>
);
}
以上代码中可以测试出来,Sub组件的 console.log 在App2组件更新时,一直被迫触发,这就是典型的性能浪费。
A. memo
使用memo这个hook可以解决这一问题:
import React, { useState, memo } from "react";
const Sub = memo(() => {
console.log("Sub被渲染了");
return <h3>Sub组件</h3>;
});
export default function App2() {
const [num, setNum] = useState<number>(0);
const changeNum = () => setNum(num + 1)
return (
<div>
<h2>num的值:{num}</h2>
<button onClick={changeNum}>累加num</button>
<Sub />
</div>
);
}
memo可以缓存组件,当组件的内容不受修改时,可以不更新该组件。
B. useCallback
但我们希望num的变化不造成Sub组件的更新:
import React, { useState, memo, useCallback } from "react";
interface ISubProps {
changeNum: () => void;
}
const Sub = memo((props: ISubProps) => {
console.log("Sub被渲染了");
return (
<>
<button onClick={props.changeNum}>累加num</button>
<h3>Sub组件</h3>
</>
);
});
export default function App2() {
const [num, setNum] = useState<number>(0);
const changeNum = useCallback(()=>{
setNum((num)=>num+1)
}, [])
return (
<div>
<h2>num的值:{num}</h2>
<Sub changeNum={changeNum} />
</div>
);
}
C. useMemo
useMemo与useCallback大致相同,只是useMemo需要在回调函数中再返回一个函数,我们称之为高阶函数:
import React, { useState, memo, useMemo } from "react";
interface ISubProps {
changeNum: () => void;
}
const Sub = memo((props: ISubProps) => {
console.log("Sub被渲染了");
return (
<>
<button onClick={props.changeNum}>累加num</button>
<h3>Sub组件</h3>
</>
);
});
export default function App2() {
const [num, setNum] = useState<number>(0);
const changeNum = useMemo(() => {
return () => setNum((num) => num + 1);
}, []);
return (
<div>
<h2>num的值:{num}</h2>
<Sub changeNum={changeNum} />
</div>
);
}
4、自定义hook
React中的hook允许我们自定义,来尝试一个简单的:
自定义一个hook,将所有的小写字母改大写。
import React from 'react'
const word = "Hello World";
function useBigWord(w: string){
return w.toUpperCase();
}
const App3 = () => {
const bigWord = useBigWord(word)
return (
<div>
<h3>小写:{word}</h3>
<h3>大写:{bigWord}</h3>
</div>
)
}
export default App3
|