函数是组件
- 函数式组件,本质就是一个常规函数,接收一个参数 props 并返回一个 reactElement
- 函数式组件中没有this和生命周期函数/state,不能使用 string ref
- 使用函数式组件时,应该尽量减少在函数中声明子函数,否则,组件每次更新时都会重新创建这个函数
hooks 常用的钩子
说在前面,hooks方法只能放在函数的最顶层,不能利用其它函数将它包起来。
useState — 赋予函数组件 状态
let [状态,修改该状态的方法] = useState(初始值);
1. 在同一个组件中可以使用 useState 定义多个状态
2. 注意 useState 返回的 setState 方法,不会进行对象合并
3. 注意 useState 返回的 setState 方法同样是异步方法
// 一个简单的例子function App(props) {const [data,setData] = useState({// 一般不会在初始化的时候传对象,只是为了涉及例子count: 1,name: "kkb"});console.log("render");let {count,name} = data;return <div><p>{name}</p><p>{count}</p><button onClick={()=>{setData({...data, // 因为不进行对象合并,所以先拷贝一份count: count + 1});console.log(data.count); // setState是一个异步方法,你会发现在这里打印出来,其实count还没有变}}>递增</button></div>
注意:useState 下的 setState 方法,也会进行浅对比 —> 也就说,在state没有改变的时候,render方法是不会进行调用的 == 组件不会进行渲染
在父子组件通讯中也要注意:
// child.jsfunction Child(props) {let {data,setData} = props;let {name,count} = data;return <div><p>{name}</p><p>{count}</p><button onClick={()=>{data.count++;setData(data); // 这里的data是引用类型,setState在进行浅对比的时候,引用地址并没有变,所以不会进行视图更新// 正确的做法setData({...data})}}>递增</button></div>}
useEffect — 挂载完成或者更新完成
副作用函数
// 1.组件挂载完成或更新完成之后执行useEffect(()=>{});// 2.组件挂载完成 或data修改引起的组件更新完之后执行useEffect(()=>{},[data]);//3.组件挂载完成useEffect(()=>{},[]);//4.//返还函数useEffect(()=>{return ()=>{console.log('返还函数!')}});挂载过程:组件挂载成功 --> 回调函数更新过程:(如果有地方只想在更新的时候执行,那么我们就要给他一个变量进行判断)组件即将更新 --> 执行 useEffect 的返还函数 --> 组件更新 --> useEffect 的回调函数卸载过程:执行 useEffect 的返还函数
tips:其实卸载的过程也可以看做更新的过程,只是在shouldComponentUpdate的时候,return的值为false,那么就会不进行下面的生命周期了(有待考证)
useRef — 获取DOM节点 、
- 获取DOM节点的时候,用法与createRef一样
- 当 useRef 中存储的是组件中的状态(或其他非关联DOM)时,组件更新 ref 中保存值并不会自动更新,需要我们手动更新<通过useRef来获取组件更新前数据>
注意:_useRef是组件内部的,不同于全局变量(多个组件共用),所以可以用来做一些跨状态的标记。function Child(props) {const {name,setName} = props;const [count,setCount] = useState(1);const prevCount = useRef(count);const countP = createRef(); // 与createRef作对比useEffect(()=>{console.log(countP);console.log(count,prevCount);prevCount.current = count; // 在组件即将更新的时候,获取count的值,这样就可以报存,上一次更新的数据},[count]);return <div><p ref={countP}>{name}</p><button onClick={()=>{setName("pika");}}>中文名</button><p>{count}</p><button onClick={()=>{setCount(count+1);}}>递增</button></div>}
自定义hooks
自定义 Hook 是一个函数,其名称以 “
use” 开头,函数内部可以调用其他的 Hook。
当我们两个函数之间的代码有共同的逻辑时,可以把这一段共享逻辑提取到一个自定义的hooks里,通过调用这个hooks减少大量的重复性代码。现在贴一个官网上面的实例:
// 官网例子import { useState, useEffect } from 'react';function useFriendStatus(friendID) {const [isOnline, setIsOnline] = useState(null);function handleStatusChange(status) {setIsOnline(status.isOnline);}useEffect(() => {ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);return () => {ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);};});return isOnline;}
这时候我们就可以在需要 FriendStatus 组件的地方为所欲为、为所欲为:
function FriendStatus(props) {const isOnline = useFriendStatus(props.friend.id);if (isOnline === null) {return 'Loading...';}return isOnline ? 'Online' : 'Offline';}
function FriendListItem(props) {const isOnline = useFriendStatus(props.friend.id);return (<li style={{ color: isOnline ? 'green' : 'black' }}>{props.friend.name}</li>);}
值得注意的是,在两个相同的组件中使用Hooks是不会共享state的,它是一种重用状态逻辑的机制,所以每次使用自定义Hooks的时候,state和副作用是完全隔离的。
一个小技巧
在JSX中插入style,一般不能用
function App() {return <Fragment><style>{`#root div {width: 200px;height: 200px;border: 2px solid red;}`}</style><div>1</div><div>2</div><div>3</div></Fragment>}
