13 React基础知识复习

86 阅读3分钟

useState 值为数组或对象

1. 值为数组,useState<Type[]>([])
2. 值为对象,初始值设为Null   
type User = {
  name: string;
};
const [user, setUser] = useState<User | null>(null);
结合可选链操作符就符合TS校验

props & children传值

// props传组件
const Icon = () => {
  return <div>icon组件</div>;
};

const A: React.FC = () => {
  return (
    <>
      <h2>A组件内容</h2>
      <B comp={Icon}>
        <C />
      </B>
    </>
  );
};
type Props = {
  children: React.ReactNode;
  comp: React.ComponentType;
};

const B: React.FC<Props> = (props) => {
  console.log(props);
  return (
    <>
      <div>
        <h3>B组件内容</h3>
        <props.comp></props.comp>
        <div>{props.children}</div>
      </div>
    </>
  );
};

React 传插槽

const A: React.FC = () => {
  const [num, setNum] = useState("点击输出"); 
  return (
    <>
      <h2>A组件内容</h2>
      <B type="primary" onClick={() => console.log("Click!!!")}>
        <h4>{num}</h4>
      </B>
    </>
  );
};
type Props = {
  type: string;
  onClick: () => void;
  children: React.ReactNode;
};

const B: React.FC<Props> = (props) => {
  console.log(props);
  return (
    <>
      <div>
        <h3>B组件内容</h3>
        <div>{props.type}</div>
        <button onClick={props.onClick}>{props.children}</button>
      </div>
    </>
  );
};

useRef和createRef

const A: React.FC = () => {
  // useRef只会在组件初始化时创建一次
  // 函数组件要使用useRef钩子函数,性能会更好
  // createRef会在组件每次重新渲染时重新创建
  const cRef = useRef<HTMLDivElement | null>(null);
  useEffect(() => {
    console.log("C", cRef.current);
  }, []);
  const handle = () => {
    // 直接在tsx调ref方法是获取不到
    cRef.current!.handleNum();
  };
  //TS类型问题没解决掉
  return (
    <>
      <h2>A组件内容</h2>
      <button onClick={handle}>C组件+1</button>
      <C ref={cRef} a="prop文本"></C>
    </>
  );
};
//
const C: React.FC = forwardRef((props, ref) => {
  const [num, setNum] = useState(11);
  function handleNum() {
    setNum(num + 1);
  }
  useImperativeHandle(ref, () => {
    return {
      num,
      handleNum,
    };
  });
  return (
    <>
      <div>
        <h3>C组件内容:{num}</h3>
        <button onClick={handleNum}>++</button>
      </div>
    </>
  );
});
// 大多数情况 fordwardRef和useImparetiveHandle应该一起使用
// 函数组件不能直接在子组件上挂ref,需要用到forwardRef()
// useRef 保持变量不被重新创建,典型就是定时器 ID
const timeId = useRef<{id: number}>(-1)
timeId.current.id = setTimeout(()=>{},1000)
// 后续在useEffet中就能够清楚

useContext & createContext

import UserContext from "@/Context/UserContext";
type User = {
  name: string;
  age: number;
};
type List = {
  id: number;
  name: string;
};
const A: React.FC = () => {
  const [info, setInfo] = useState<User>({ name: "", age: 0 });
  const [list, setList] = useState<List[]>([]);

  return (
    <>
      <UserContext.Provider value={{ ...info, list }}>
        <h2>A组件内容</h2>
        <C></C>
      </UserContext.Provider>
    </>
  );
};
import { createContext } from "react";
type List = {
  id: number;
  name: string;
};
type UserContextType = {
  name: string;
  age: number;
  list: List[];
}
const defaultValue:UserContextType = {
  name: '',
  age: 0,
  list: []
  // add:()=>void
}
const UserContext = createContext(defaultValue)

export default UserContext
import UserContext from "@/Context/UserContext";
import { useContext } from "react";

const C = () => {
  const ctx = useContext(UserContext);
  console.log(ctx);
  return (
    <>
      <div>C组件</div>
      <div>
        <span>展示父组件的数据:</span>
        <span>{ctx.name}</span>
        <span>{ctx.age}</span>
        {ctx.list?.map((item, index) => {
          return <div key={index}>{item.name}</div>;
        })}
      </div>
    </>
  );
};
// 第二种写法
import { createContext } from "react";
type IContext = {
  list: Array<IGoods> |undefined;
  handleList:(id: number, type: string)=>void
  handleList2: (id: number,val: number)=>void
}
const defaultVal = {
  list:[],
  handleList(){},
  handleList2(){}
}
 const GoodsContext = createContext<IContext>(defaultVal)

 export default GoodsContext
 //
 const handleList2 = (id: number, val: number) => {
    const item = list.find((item) => item.id === id);
    item!.count = val;
    setList([...list]);
};
//
<GoodsContext.Provider value={{ list, handleList, handleList2 }}>
// 把setList放到函数里,上面这种传方法,就能直接更改数据

React-Redux & Toolkit

import { Provider } from "react-redux";
import store from "../../store/index";

const A: React.FC = () => {
  return (
    <>
      <Provider store={store}>
        <h2>A组件内容</h2>
        <C></C>
      </Provider>
    </>
  );
};
import { createSlice } from "@reduxjs/toolkit";
const userStore = createSlice({
  name: 'user',
  initialState:{
    name: '未命名',
    age: 20,
    infos: [1,2,3]
  },
  reducers:{
    handleName(state, action){
      // 打印看看就知道了,如何结构看自己传的什么
      console.log('action', action);
      state.name = action.payload.name
    },
    // 注意看,下面的函数用的break, 不是return 
    handleAge(state, action){
      const {type, num} = action.payload
      switch(type){
        case 'add':
           state.age +=num;
           break;
        case 'del':
           state.age -=num;
           break;

        default:
           state.age;
           break;
      }
    },
    handleInfo(state, action){
      // state.infos = action.payload
      state.infos = [...state.infos, action.payload]
    }
  }
})
export const  {handleName, handleAge, handleInfo} =  userStore.actions
export default userStore.reducer
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./module/userStore";

const store = configureStore({
  reducer: {
    userReducer
  }
})
export default store
import { useSelector, useDispatch } from "react-redux";
import { handleName, handleAge, handleInfo } from "@/store/module/userStore";

type UserReducer = {
  userReducer: {
    name: string;
    age: number;
  };
};

const C = () => {
  const { name, age, infos } = useSelector(
    (state: UserReducer) => state.userReducer
  );
  const dispatch = useDispatch();
  const handleSyncName = (name: string) => {
    dispatch(handleName({ name }));
  };
  const handleSyncAge = (type: "del" | "add", num: number) => {
    dispatch(handleAge({ type, num }));
  };
  const handleSyncInfo = (info: []) => {
    dispatch(handleInfo(info));
  };
  // setTimeout(() => {
  //   handleSyncName("张三");
  //   handleSyncAge("add", 10);
  //   handleSyncInfo(999);
  // }, 3000);
  return (
    <>
      <div>C组件</div>
      <h2>toolkit内容</h2>
      <div>
        <div>姓名:{name}</div>
        <div>年龄:{age}</div>
        {infos.map((item, i) => {
          return <div key={i}>值{item}</div>;
        })}
      </div>
    </>
  );
};

React-Router-DOM V6

const routerConfig = createBrowserRouter([
  { path: "/", element: <Navigate to="/Login"></Navigate> },
  { path: "/login", element: <Login /> },
  {
    path: "/main",
    element: <Main />,
    children: [{ index: true, element: <MainSon /> }],
    // 去掉path 改为 index: true,为默认显示
  },
  { path: "*", element: <NotFound /> },
]);
// 根组件下展示
<RouterProvider router={routerConfig}></RouterProvider>
const Main: React.FC = () => {
  const navigate = useNavigate();
  const [params] = useSearchParams();
  const _id = params.get("id");

  const paramsObj = useParams();
  // params参数需要在路由表加 /:v 占位
  const gotoLogin = () => {
    navigate("/login");
  };
  // 父子组件共享context数据
  const [name, setName] = React.useState("父组件数据");
  return (
    <>
      <h3>Main组件</h3>
      <div>
        <button onClick={gotoLogin}>去Login</button>
        <div>
          <p>SearchParams:{_id}</p>
          <p>{paramsObj.v}</p>
        </div>
        <h4>二级路由展示区</h4>
        <hr />
        <Outlet context={[name, setName]} />
      </div>
    </>
  );
};
const Login: React.FC = () => {
  const navigate = useNavigate();
  const [id, setId] = useState(99);
  const [name, setName] = useState("张三");
  const gotoMain = () => {
    navigate(`/main?id=${id}`);
  };
  const gotoMain2 = () => {
    navigate(`/main/${name}`);
  };
  // 可以有下面的配置和state传参
  // navigate('/main',{replace:true,state:{name:'666'}})
  // 在home页面的组件中可以使用useLocation获取到传过去的state
  // 也可以navigate(-1)
  return (
    <>
      <h3>Login组件</h3>
      <div>
        <button onClick={gotoMain}>去Main</button>
        <button onClick={gotoMain2}>去Main2</button>
      </div>
    </>
  );
};
const MainSon: React.FC = () => {
  const [name, setName] = useOutletContext();
  return (
    <>
      <p>{name}</p>
      <button onClick={() => setName("lisi")}>修改</button>
    </>
  );
};

useReducer 用法

type Action = {
  type: "ADD" | "DEL";
  payload: number;
};
function countReducer(state: number, action: Action) {
  switch (action.type) {
    case "ADD":
      return state + action.payload;
    case "DEL":
      return state - action.payload;
    default:
      return state;
  }
}
const A1: React.FC = () => {
  const [count, dispatch] = useReducer(countReducer, 0);
  // 这个函数可以不用定义,这样可以直接使用类型推导
  function handleCount(type: Action["type"], num: number) {
    dispatch({ type, payload: num });
  }
  return (
    <>
      <h2>{count}</h2>
      <button onClick={() => dispatch({ type: "ADD", payload: 100 })}>
        操作+
      </button>
    </>
  );
};