函数是组件

  • 函数式组件,本质就是一个常规函数,接收一个参数 props 并返回一个 reactElement
  • 函数式组件中没有this和生命周期函数/state,不能使用 string ref
  • 使用函数式组件时,应该尽量减少在函数中声明子函数,否则,组件每次更新时都会重新创建这个函数

hooks 常用的钩子

说在前面,hooks方法只能放在函数的最顶层,不能利用其它函数将它包起来。

useState — 赋予函数组件 状态

let [状态,修改该状态的方法] = useState(初始值);
1. 在同一个组件中可以使用 useState 定义多个状态
2. 注意 useState 返回的 setState 方法,不会进行对象合并
3. 注意 useState 返回的 setState 方法同样是异步方法

  1. // 一个简单的例子
  2. function App(props) {
  3. const [data,setData] = useState({
  4. // 一般不会在初始化的时候传对象,只是为了涉及例子
  5. count: 1,
  6. name: "kkb"
  7. });
  8. console.log("render");
  9. let {count,name} = data;
  10. return <div>
  11. <p>{name}</p>
  12. <p>{count}</p>
  13. <button onClick={()=>{
  14. setData({
  15. ...data, // 因为不进行对象合并,所以先拷贝一份
  16. count: count + 1
  17. });
  18. console.log(data.count); // setState是一个异步方法,你会发现在这里打印出来,其实count还没有变
  19. }}>递增</button>
  20. </div>

注意:useState 下的 setState 方法,也会进行浅对比 —> 也就说,在state没有改变的时候,render方法是不会进行调用的 == 组件不会进行渲染
在父子组件通讯中也要注意:

  1. // child.js
  2. function Child(props) {
  3. let {data,setData} = props;
  4. let {name,count} = data;
  5. return <div>
  6. <p>{name}</p>
  7. <p>{count}</p>
  8. <button onClick={()=>{
  9. data.count++;
  10. setData(data); // 这里的data是引用类型,setState在进行浅对比的时候,引用地址并没有变,所以不会进行视图更新
  11. // 正确的做法
  12. setData({...data})
  13. }}>递增</button>
  14. </div>
  15. }

useEffect — 挂载完成或者更新完成

副作用函数

  1. // 1.组件挂载完成或更新完成之后执行
  2. useEffect(()=>{});
  3. // 2.组件挂载完成 或data修改引起的组件更新完之后执行
  4. useEffect(()=>{},[data]);
  5. //3.组件挂载完成
  6. useEffect(()=>{},[]);
  7. //4.//返还函数
  8. useEffect(()=>{
  9. return ()=>{console.log('返还函数!')}
  10. });
  11. 挂载过程:
  12. 组件挂载成功 --> 回调函数
  13. 更新过程:(如果有地方只想在更新的时候执行,那么我们就要给他一个变量进行判断)
  14. 组件即将更新 --> 执行 useEffect 的返还函数 --> 组件更新 --> useEffect 的回调函数
  15. 卸载过程:
  16. 执行 useEffect 的返还函数

tips:其实卸载的过程也可以看做更新的过程,只是在shouldComponentUpdate的时候,return的值为false,那么就会不进行下面的生命周期了(有待考证)

useRef — 获取DOM节点 、

  • 获取DOM节点的时候,用法与createRef一样
  • 当 useRef 中存储的是组件中的状态(或其他非关联DOM)时,组件更新 ref 中保存值并不会自动更新,需要我们手动更新<通过useRef来获取组件更新前数据>
    1. function Child(props) {
    2. const {name,setName} = props;
    3. const [count,setCount] = useState(1);
    4. const prevCount = useRef(count);
    5. const countP = createRef(); // 与createRef作对比
    6. useEffect(()=>{
    7. console.log(countP);
    8. console.log(count,prevCount);
    9. prevCount.current = count; // 在组件即将更新的时候,获取count的值,这样就可以报存,上一次更新的数据
    10. },[count]);
    11. return <div>
    12. <p ref={countP}>{name}</p>
    13. <button onClick={()=>{
    14. setName("pika");
    15. }}>中文名</button>
    16. <p>{count}</p>
    17. <button onClick={()=>{
    18. setCount(count+1);
    19. }}>递增</button>
    20. </div>
    21. }
    注意:_useRef是组件内部的,不同于全局变量(多个组件共用),所以可以用来做一些跨状态的标记。

自定义hooks

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。

当我们两个函数之间的代码有共同的逻辑时,可以把这一段共享逻辑提取到一个自定义的hooks里,通过调用这个hooks减少大量的重复性代码。现在贴一个官网上面的实例:

  1. // 官网例子
  2. import { useState, useEffect } from 'react';
  3. function useFriendStatus(friendID) {
  4. const [isOnline, setIsOnline] = useState(null);
  5. function handleStatusChange(status) {
  6. setIsOnline(status.isOnline);
  7. }
  8. useEffect(() => {
  9. ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
  10. return () => {
  11. ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
  12. };
  13. });
  14. return isOnline;
  15. }

这时候我们就可以在需要 FriendStatus 组件的地方为所欲为、为所欲为:

  1. function FriendStatus(props) {
  2. const isOnline = useFriendStatus(props.friend.id);
  3. if (isOnline === null) {
  4. return 'Loading...';
  5. }
  6. return isOnline ? 'Online' : 'Offline';
  7. }
  1. function FriendListItem(props) {
  2. const isOnline = useFriendStatus(props.friend.id);
  3. return (
  4. <li style={{ color: isOnline ? 'green' : 'black' }}>
  5. {props.friend.name}
  6. </li>
  7. );
  8. }

值得注意的是,在两个相同的组件中使用Hooks是不会共享state的,它是一种重用状态逻辑的机制,所以每次使用自定义Hooks的时候,state和副作用是完全隔离的。

一个小技巧

在JSX中插入style,一般不能用

  1. function App() {
  2. return <Fragment>
  3. <style>
  4. {`
  5. #root div {
  6. width: 200px;
  7. height: 200px;
  8. border: 2px solid red;
  9. }
  10. `}
  11. </style>
  12. <div>1</div>
  13. <div>2</div>
  14. <div>3</div>
  15. </Fragment>
  16. }