react Hooks

132 阅读11分钟
1、react两大类

class与function(函数)

2、  react函数类与普通函数区别

react函数类承接ui渲染与更新ui

3、  react通信方式有那五类

props和callback、context(跨层)、event事件、ref传递、状态管理

4、  react父传子、子传父

props和callback

父传子props

import {useState} from 'react';
const Father =()=>{
    const [flag, setFlag]= useState<boolen>(true);
    return(
    <>
    <div>我是父组件</div>
    <Son son='父组件'/>
    </>)
}
const Son = (props:{son:string})=>{
const {son} = props
return (<div>{son}</div>);
}
export default Father

子传父callback通知

import {useState} from 'react';
import { Button } from 'antd';

Father =()=>{
    const [number, setNumber]= useState<number>(1);
    return(
    <>
    <div>我是父组件</div>
    <Son son={(number)=>{
    setNumber();
    }}/>
    </>)
}
const Son = ({son})=>{
const [number, setNumber] = useState<number>(0)
return (<div>我是子组件</div>
    <button onClik={()=>{
    const res = number+1
    setNumber(res)
    son(res)
    }}></button>
);
}
export default Father
5、  跨层传递context方式

创建context方法:React.createContext();

外界数据链接通过Provider

消费层Consumer

import {useState, Componentfrom 'react';
import {Btton CheckboxInput } from 'antd';
//创建context方法
const TheContext = React.createContext();
//主体颜色
    const theme = {
      dark: {
        color"#5B8FF9",
        background"#5B8FF9",
        border"1px solid #5B8FF9",
        type"dark",
        buttomType"primary",
      },
      light: {
        color"#E86452",
        background"#E86452",
        border"1px solid #E86452",
        type"light",
        buttomType"default",
      },
    };
    const Father =()=>{
    const [theContext, setTheContext] = useState(theme.dark);
    return (
        <ThemeContext.Provider
          value={{ ...themeContextValuesetTheme: setThemeContext }}
        >
          <Child />
        </ThemeContext.Provider>
      );
    }
 class Child extends Component<anyany> {
      static contextType = ThemeContext;
      render() {
        const { border, setTheme, color, background, buttomType }: any =
          this.context;
        return (
          <div style={{ bordercolorpadding: 20 }}>
            <div>
              <span> 选择主题: </span>
              <CheckboxView
                label="主题1"
                name="dark"
                onChange={() => setTheme(theme.dark)}
              />
              <CheckboxView
                label="主题2"
                name="light"
                onChange={() => setTheme(theme.light)}
              />
            </div>
            <div style={{ colormarginTop: 8 }}>
              大家好,我是小杜杜,一起玩转Hooks吧!
            </div>
            <div style={{ marginTop: 8 }}>
              <Input
                placeholder="请输入你的名字"
                style={{ colorbordermarginBottom: 10 }}
              />
              <Button type={buttomType}>提交</Button>
            </div>
          </div>
        );
      }
    }

    class CheckboxView extends Component<anyany> {
      static contextType = ThemeContext;

      render() {
        const { label, name, onChange } = this.props;
        const { color, type }: any = this.context;

        return (
          <div
            style={{
              display: "inline-block",
              marginLeft: 10,
            }}
          >
            <Checkbox checked={type === name} style={{ color }} onChange={onChange}>
              {label}
            </Checkbox>
          </div>
        );
      }
    }

    export default Father;    
6、强化组件的几种方式

extends继承、高阶组件模式、自定义Hooks模式,已废弃的mixin模式

7、  extends继承

一般用于class方式中,通过React.Component或者React.PurComponent中,用于强化组件

   import React from "react";
    import { Button } from "antd";

    class Child extends React.Component<anyany> {
      constructor(props: any) {
        super(props);
        this.state = {
          msg"大家好,我是小杜杜,一起玩转Hooks吧!",
        };
      }

      speak() {
        console.log("Child中的speak");
      }

      render() {
        return (
          <>
            <div>{this.state.msg}</div>
            <Button type="primary" onClick={() => this.speak()}>
              查看控制台
            </Button>
          </>
        );
      }
    }

    class Index extends Child {
      speak() {
        console.log("extends 模式,强化后会替代Child的speak方法");
      }
    }

    export default Index;
8、  高级组件(HOC)模式

高阶组件类似于extends都是组件强化组件,HOC 可以做很多事情,比如强化 props、条件渲染、性能优化、事件赋能、反向继承等

    import React, { useState } from "react";
    import { Button } from "antd";

    const HOC = (Component: any) => (props: any) => {
      return (
        <Component
          name={"大家好我是小杜杜一起玩转Hooks吧!"}
          {...props}
        ></Component>
      );
    };

    const IndexReact.FC<any> = (props) => {
      const [flag, setFlag] = useState<boolean>(false);

      return (
        <div>
          <Button type="primary" onClick={() => setFlag(true)}>
            获取props
          </Button>
          {flag && <div>{JSON.stringify(props)}</div>}
        </div>
      );
    };

    export default HOC(Index);
9、  hooks较class组件优缺点

class三大缺点:繁琐的super强继承逻辑、奇怪this指向不止直接使用箭头函数、繁琐的什么周期(componentDidMount、getDerivedStateFromProps)等九个API

hooks有点:更好的状态复用、友好的替代

10、  hooks的十种API
1、  useState:让hooks的函数组件更新储存数据使用,类似于calss类的this.setState
const [data, setData] = useState('');//可存储数组、object、string、number、function类型
2、  useEffect:数据更新组件,类似于class的componentDidMount组件
import {useState, useEffect} from 'react';
const APP =()=>{
 const [number, setNumber] = useState(0);
const [count, setCount] = useState(0);
useEffect(()=>{
console.log('count执行才会执行')
},[count]);//()=>{}destory[]dep
retrun(
 <>
      <div>
        number: {number} count: {count}
      </div>
      <Button type="primary" onClick={() => setNumber((v) => v + 1)}>
        number + 1
      </Button>
      <Button
        type="primary"
        style={{ marginLeft: 10 }}
        onClick={() => setCount((v) => v + 1)}
      >
        count + 1
      </Button>
    </>)
}

useEffect分为destory和dep

destory监听addEventListener 和 removeEventListener

dep决定callback时机,dep不存在会无限制执行

3、  useReducer:是一个升级版的useState

是一个惰性组件,当state和state值相同时,read不更新

const IndexReact.FC<any> = () => {
  console.log("父组件发生更新");
  ...
  return (
    <>
        ...
      <Button
        type="primary"
        style={{ marginLeft: 10 }}
        onClick={() => dispatch({ type: "no", payload: 1 })}
      >
        无关按钮
      </Button>
      <Child count={count} />
    </>
  )
};

const ChildReact.FC<any> = ({ count }) => {
  console.log("子组件发生更新");
  return <div>在子组件的count:{count}</div>;
};
4、  useContext:类似于context组件用于跨层使用
const dataUseContext = userContext(context);//context传入参数

用于组件间的数据共享父、子、孙

import { useState, createContext, useContext } from "react";
import { Button } from "antd";

const CountContext = createContext(-1);

const Child = () => {
  const count = useContext(CountContext);

  return (
    <div style={{ marginTop: 10 }}>
      子组件获取到的count: {count}
      <Son />
    </div>
  );
};

const Son = () => {
  const count = useContext(CountContext);

  return <div style={{ marginTop: 10 }}>孙组件获取到的count: {count}</div>;
};

const IndexReact.FC<any> = () => {
  let [count, setCount] = useState(0);

  return (
    <>
      <div>父组件中的count:{count}</div>
      <Button type="primary" onClick={() => setCount((v) => v + 1)}>
        点击+1
      </Button>
      <CountContext.Provider value={count}>
        <Child />
      </CountContext.Provider>
    </>
  );
};

export default Index;
5、  useMemo:减少不要重绘设计
const DatauseMemo = useMemo(fn,deps);

fn函数的返回值会作为缓存志

deps依赖项,数组,会通过数组里面的值来判断进行fn的调整,如果发生变化,则会得到新的缓存

//案例
const usePow = (list: number[]) => {
  return useMemo(
    () =>
      list.map((item: number) => {
        console.log(1);
        return Math.pow(item, 2);
      }),
    []
  );
};
6、  useCallback:与useMemo类似,不同点是useMemo返回的是值,useCallback返回的是个函数
const resfn = useCallback(fn,deps);

fn:函数的返回值作为缓存值

deps:依赖项,数组会通过数组来判断fn的调用,如果依赖项变化会改变

resfn :更新之后的数据源,即fn函数,如果deps中历来发生变化,将重新fn,没变化取上一次函数

import { useState, useCallback, memo } from "react";
import { Button } from "antd";

const IndexReact.FC<any> = () => {
  let [count, setCount] = useState(0);
  let [flag, setFlag] = useState(true);

  const add = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <>
      <TestButton onClick={() => setCount((v) => v + 1)}>普通点击</TestButton>
      <TestButton onClick={add}>useCallback点击</TestButton>
      <div>数字:{count}</div>
      <Button type="primary" onClick={() => setFlag((v) => !v)}>
        切换{JSON.stringify(flag)}
      </Button>
    </>
  );
};

const TestButton = memo(({ children, onClick = () => {} }: any) => {
  console.log(children);
  return (
    <Button
      type="primary"
      onClick={onClick}
      style={children === "useCallback点击" ? { marginLeft: 10 } : undefined}
    >
      {children}
    </Button>
  );
});

export default Index;
7.  useRef:获取当前绑定元素所有属性
const ref= useRef(initVlaue);

initVlaue初始值,默认值

ref返回的值在current的对象,需要通过current这个对象获取

import React from "react";
import { useRef, useState } from "react";

const TestDemo =()=>{
    const scrollRef = useRef(null);
    const [clientHeight, setClientHeight] = useState(0);
    const [scrollTop, setScrollTop] = useState(0);
    const [scrollHeight, setScrollHeight] = useState(0);
  
    const onScroll = () => {
      if (scrollRef?.current) {
        let clientHeight = scrollRef?.current.clientHeight; //可视区域高度
        let scrollTop = scrollRef?.current.scrollTop; //滚动条滚动高度
        let scrollHeight = scrollRef?.current.scrollHeight; //滚动内容高度
        setClientHeight(clientHeight);
        setScrollTop(scrollTop);
        setScrollHeight(scrollHeight);
      }
    };
  
    return (
      <>
        <div>
          <p>可视区域高度:{clientHeight}</p>
          <p>滚动条滚动高度:{scrollTop}</p>
          <p>滚动内容高度:{scrollHeight}</p>
        </div>
        <div
          style={{ height: 200, border: "1px solid #000", overflowY: "auto" }}
          ref={scrollRef}
          onScroll={onScroll}
        >
          <div style={{ height: 2000 }}></div>
        </div>
      </>
    );
  };

export default TestDemo;
8、useImperativeHandle:可以通过 ref 或 forwardRef 暴露给父组件的实例值,所谓的实例值是指值和函数
useImperativeHandle(ref,createHandle, deps)

ref:接受 useRef 或 forwardRef 传递过来的 ref;

createHandle:处理函数,返回值作为暴露给父组件的 ref 对象;

deps:依赖项,依赖项如果更改,会形成新的 ref 对象

import React from "react";
import { useRef, useState, useImperativeHandle } from "react";

const Son = ({cRef}) => {

  const [count, setCount] = useState(0)

  useImperativeHandle(cRef, () => ({
    add
  }))

  const add = () => {
    setCount((v) => v + 1)
  }

  return <div>
    <p>点击次数:{count}</p>
    <button onClick={() => add()}> 子组件的按钮,点击+1</button>
  </div>
}
const TestDemo =()=>{
    const scrollRef = useRef(null);
    
  
    return (
      <>
        <div>
            我是父组件
            <button
        type="primary"
        onClick={() =>  scrollRef.current.add()}
      >
        父组件上的按钮,点击+1
      </button>
        </div>
          <Son cRef={scrollRef}/>
      </>
    );
  };

export default TestDemo;
9、  useLayoutEffect与useEffect方法一样,useLayoutEffect它是同步执行
import { useState, useEffect, useLayoutEffect } from "react";

const IndexReact.FC<any> = () => {
  let [count, setCount] = useState(0);
  let [count1, setCount1] = useState(0);

  useEffect(() => {
    if(count === 0){
      setCount(10 + Math.random() * 100)
    }
  }, [count])

  useLayoutEffect(() => {
    if(count1 === 0){
      setCount1(10 + Math.random() * 100)
    }
  }, [count1])

  return (
    <>
      <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
      <div>useEffect的count:{count}</div>
      <div>useLayoutEffect的count:{count1}</div>
    </>
  );
};

export default Index;

之所有用的少是因为同步执行容易阻塞线程,userEffect是异步不会有这种问题

10、useDebugValue用于检查自定义hooks
11、  hooks常用的五种API
1、useSynExternalStore用户强制状态更新,使得外部store支持并发读取
const state = useSynExternalStore(
 subscribe,
getSnapshot,
getServerSnapshot
)

subscribe:订阅函数,用于注册一个回调函数,当存储值发生更改时被调用。 此外,useSyncExternalStore 会通过带有记忆性的 getSnapshot 来判断数据是否发生变化,如果发生变化,那么会强制更新数据;

getSnapshot:返回当前存储值的函数。必须返回缓存的值。如果 getSnapshot 连续多次调用,则必须返回相同的确切值,除非中间有存储值更新;

getServerSnapshot:返回服务端(hydration 模式下)渲染期间使用的存储值的函数

import { useSyncExternalStore } from "react";
import { Button } from "antd";
import { combineReducers, createStore } from "redux";

const reducer = (state: number = 1, action: any) => {
  switch (action.type) {
    case "ADD":
      return state + 1;
    case "DEL":
      return state - 1;
    default:
      return state;
  }
};

/* 注册reducer,并创建store */
const rootReducer = combineReducers({ count: reducer });
const store = createStore(rootReducer, { count1 });

const IndexReact.FC<any> = () => {
  //订阅
  const state = useSyncExternalStore(
    store.subscribe,
    () => store.getState().count
  );

  return (
    <>
      <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
      <div>数据源: {state}</div>
      <Button type="primary" onClick={() => store.dispatch({ type: "ADD" })}>
        加1
      </Button>
      <Button
        style={{ marginLeft: 8 }}
        onClick={() => store.dispatch({ type: "DEL" })}
      >
        减1
      </Button>
    </>
  );
};

export default Index;
2、  useTransition:返回一个状态值标识过渡更新任务等待
const [isPending, startTransition] = useTransition();

isPending:布尔值,过渡状态的标志,为 true 时表示等待状态;

startTransition:可以将里面的任务变成过渡更新任务。

import { useState, useTransition } from "react";
import { Input } from "antd";

const Index: React.FC<any> = () => {
  const [isPending, startTransition] = useTransition();
  const [input, setInput] = useState("");
  const [list, setList] = useState<string[]>([]);

  return (
    <>
      <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
      <Input
        value={input}
        onChange={(e) => {
          setInput(e.target.value);
          startTransition(() => {
            const res: string[] = [];
            for (let i = 0; i < 10000; i++) {
              res.push(e.target.value);
            }
            setList(res);
          });
        }}
      />
      {isPending ? (
        <div>加载中...</div>
      ) : (
        list.map((item, index) => <div key={index}>{item}</div>)
      )}
    </>
  );
};

export default Index;
3.  useDeferredValue可以让状态滞后派生,与 useTransition 功能类似,推迟屏幕优先级不高的部分
const deFerredValue = useDeferredValue(value);

value:接受一个可变的值,如useState所创建的值。

deferredValue:返回一个延迟状态的值。

import { useState, useDeferredValue } from "react";
import { Input } from "antd";

const getList = (key: any) => {
  const arr = [];
  for (let i = 0; i < 10000; i++) {
    if (String(i).includes(key)) {
      arr.push(<li key={i}>{i}</li>);
    }
  }
  return arr;
};

const IndexReact.FC<any> = () => {
  //订阅
  const [input, setInput] = useState("");
  const deferredValue = useDeferredValue(input);
  console.log("value:", input);
  console.log("deferredValue:", deferredValue);

  return (
    <>
      <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
      <Input value={input} onChange={(e: any) => setInput(e.target.value)} />
      <div>
        <ul>{deferredValue ? getList(deferredValue) : null}</ul>
      </div>
    </>
  );
};

export default Index;
4、  useInsertionEffect: 与 useEffect 一样,但它在所有 DOM 突变之前同步触发
import { useInsertionEffect } from "react";

const IndexReact.FC<any> = () => {
  useInsertionEffect(() => {
    const style = document.createElement("style");
    style.innerHTML = `
      .css-in-js{
        color: blue;
      }
    `;
    document.head.appendChild(style);
  }, []);

  return (
    <div>
      <div className="css-in-js">大家好,我是小杜杜,一起玩转Hooks吧!</div>
    </div>
  );
};

export default Index;
5、  useId用于创建唯一ID
const id = useId();

12、  响应式开发useState

hooks通过改变数据源来推动整个数据改变状态,常用的useState、useReducer

const [name,setName] =useState(xxx);
 useLatest

useLatest:值永远是最新值,可以避免闭包问题

const ref = useLatest(count);
 
    useEffect(() => {
        const interval = setInterval(() => {
          console.log("count:", count);
          console.log("ref:", ref);
          setCount(ref.current + 1);
        }, 1000);
        return () => clearInterval(interval);
    }, []);
useMount和useUnmout

useMount只在组件初始化化执行hook

useUnmount只在组件卸载时hook

import { useState } from "react";
import { useMount, useUnmount } from "../../hooks";

import { Button, message } from "antd";

const Child = () => {
  useMount(() => {
    message.info("首次渲染");
  });

  useUnmount(() => {
    message.info("组件已卸载");
  });

  return <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>;
};

const Index = () => {
  const [flag, setFlag] = useState<boolean>(false);

  return (
    <div>
      <Button type="primary" onClick={() => setFlag((v) => !v)}>
        切换 {flag ? "unmount" : "mount"}
      </Button>
      {flag && <Child />}
    </div>
  );
};

export default Index;
useUnmountedRef

 获取当前组件是否卸载,这个钩子的思路也很简单,只需要利用 useEffect 的状态,来保存对应的值就 ok 了

import { useState } from "react";
import { useUnmountedRef, useUnmount, useMount } from "../../hooks";
import { Button } from "antd";

const Child = () => {
  const unmountedRef = useUnmountedRef();

  useMount(() => {
    console.log("初始化:", unmountedRef);
  });
  useUnmount(() => {
    console.log("卸载:", unmountedRef);
  });

  return <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>;
};

const Index = () => {
  const [flag, setFlag] = useState<boolean>(false);

  return (
    <div>
      <Button type="primary" onClick={() => setFlag((v) => !v)}>
        切换 {flag ? "卸载" : "初始化"}
      </Button>
      {flag && <Child />}
    </div>
  );
};

export default Index;
useSafeState

useSafeState:使用方法与 useState 的用法完全一致,但在组件卸载后异步回调内的 setState 不再执行,这样可以避免因组件卸载后更新状态而导致的内存泄漏。

import { useCallback, useState } from "react";
import type { DispatchSetStateAction } from "react";
import useUnmountedRef from "../useUnmountedRef";

function useSafeState<S>(
  initialState: S | (() => S)
): [S, Dispatch<SetStateAction<S>>];
function useSafeState<S = undefined>(): [
  S | undefined,
  Dispatch<SetStateAction<S | undefined>>
];
function useSafeState<S>(initialState?: S | (() => S)) {
  const unmountedRef: { currentboolean } = useUnmountedRef();
  const [state, setState] = useState(initialState);
  const setCurrentState = useCallback((currentState: any) => {
    if (unmountedRef.currentreturn;
    setState(currentState);
  }, []);

  return [state, setCurrentState] as const;
}

export default useSafeState;
useUpdata

强制组件重新渲染,最终返回一个函数。

import { useUpdate } from "../../hooks";
import { Button, message } from "antd";

const Index = () => {
  const update = useUpdate();

  return (
    <div>
      <div>时间:{Date.now()}</div>
      <Button
        type="primary"
        onClick={() => {
          update();
        }}
      >
        更新
      </Button>
    </div>
  );
};

export default Index;
useCreation

强化 useMemo 和 useRef,用法与 useMemo 一样,一般用于性能优化

如何增强:

● useMemo 的第一个参数 fn,会缓存对应的值,那么这个值就有可能拿不到最新的值,而 useCreation 拿到的值永远都是最新值;

● useRef 在创建复杂常量的时候,会出现潜在的性能隐患(如:实例化 new Subject),但 useCreation 可以有效地避免。

import React, { useState } from "react";
import { Button } from "antd";
import { useCreation } from "../../hooks";

const IndexReact.FC<any> = () => {
  const [flag, setFlag] = useState<boolean>(false);

  const getNowData = () => {
    return Math.random();
  };

  const nowData = useCreation(() => getNowData(), []);

  return (
    <>
      <div>正常的函数: {getNowData()}</div>
      <div>useCreation包裹后的: {nowData}</div>
      <Button
        type="primary"
        onClick={() => {
          setFlag((v) => !v);
        }}
      >
        切换状态{JSON.stringify(flag)}
      </Button>
    </>
  );
};

export default Index;
useReactive

一种具备响应式的 useState,用法与 useState 类似,但可以动态地设置值。

import { useReactive } from "../../hooks";
import { Button, Input } from "antd";

const Index = () => {
  const state = useReactive<any>({
    count: 0,
    name: "大家好,我是小杜杜,一起玩转Hooks吧!",
    flag: true,
    arr: [],
    bugs: ["小杜杜", "react", "hook"],
    addBug(bug: string) {
      this.bugs.push(bug);
    },
    get bugsCount() {
      return this.bugs.length;
    },
  });

  return (
    <div>
      <div style={{ fontWeight: "bold" }}>基本使用:</div>
      <div style={{ marginTop: 8 }}> 对数字进行操作:{state.count}</div>
      <div
        style={{
          margin: "8px 0",
          display: "flex",
          justifyContent: "flex-start",
        }}
      >
        <Button type="primary" onClick={() => state.count++}>
          加1
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => state.count--}
        >
          减1
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => (state.count = 7)}
        >
          设置为7
        </Button>
      </div>
      <div style={{ marginTop: 8 }}> 对字符串进行操作:{state.name}</div>
      <div
        style={{
          margin: "8px 0",
          display: "flex",
          justifyContent: "flex-start",
        }}
      >
        <Button type="primary" onClick={() => (state.name = "小杜杜")}>
          设置为小杜杜
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => (state.name = "Domesy")}
        >
          设置为Domesy
        </Button>
      </div>
      <div style={{ marginTop: 8 }}>
        {" "}
        对布尔值进行操作:{JSON.stringify(state.flag)}
      </div>
      <div
        style={{
          margin: "8px 0",
          display: "flex",
          justifyContent: "flex-start",
        }}
      >
        <Button type="primary" onClick={() => (state.flag = !state.flag)}>
          切换状态
        </Button>
      </div>
      <div style={{ marginTop: 8 }}>
        {" "}
        对数组进行操作:{JSON.stringify(state.arr)}
      </div>
      <div
        style={{
          margin: "8px 0",
          display: "flex",
          justifyContent: "flex-start",
        }}
      >
        <Button
          type="primary"
          onClick={() => state.arr.push(Math.floor(Math.random() * 100))}
        >
          push
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => state.arr.pop()}
        >
          pop
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => state.arr.shift()}
        >
          shift
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => state.arr.unshift(Math.floor(Math.random() * 100))}
        >
          unshift
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => state.arr.reverse()}
        >
          reverse
        </Button>
        <Button
          type="primary"
          style={{ marginLeft: 8 }}
          onClick={() => state.arr.sort()}
        >
          sort
        </Button>
      </div>
      <div style={{ fontWeight: "bold", marginTop: 8 }}>计算属性:</div>
      <div style={{ marginTop: 8 }}>数量:{state.bugsCount} 个</div>
      <div style={{ margin: "8px 0" }}>
        <form
          onSubmit={(e) => {
            state.bug ? state.addBug(state.bug) : state.addBug("domesy");
            state.bug = "";
            e.preventDefault();
          }}
        >
          <Input
            type="text"
            value={state.bug}
            style={{ width: 200 }}
            onChange={(e) => (state.bug = e.target.value)}
          />
          <Button type="primary" htmlType="submit" style={{ marginLeft: 8 }}>
            增加
          </Button>
          <Button style={{ marginLeft: 8 }} onClick={() => state.bugs.pop()}>
            删除
          </Button>
        </form>
      </div>
      <ul>
        {state.bugs.map((bug: any, index: number) => (
          <li key={index}>{bug}</li>
        ))}
      </ul>
    </div>
  );
};

export default Index;