基础知识
1. 项目的初始化
- 初始化项目
npx create-react-app my-app --template Typescript
- vite初始化
yarn create vite
React
2. JSX的命名约定
- 小驼峰命名
<div className="style"></div>
- 自定义属性
<div data-c="1"></div>
3. JSX中的模块化CSS
- 配置声名文件
declare module "*.css"{
const css:{
[key:string]:string};
export default css;
}
- 修改命名*.module.css引入
import Appcss from './App.module.css'
(1)css类型的实现(Typescript)
- 安装插件依赖
yarn add typescript-plugin-css-modules -S
- 配置插件
{
"compilerOptions": {
...
"plugins": [{
"name": "typescript-plugin-css-modules"}]
},
"include": ["src"],
"references": [{
"path": "./tsconfig.node.json" }]
}
- 添加文件夹配置
.vscode/settings.json
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
4. 组件的类型
- 函数式组件
import React from "react";
import rebot from './robot.module.css'
interface RobotProps{
id:number,
name:string,
email:string
}
const Robot:React.FC<RobotProps>=({id,name,email})=>{
return <div className={rebot.cardContainer}>
<img src={`https://robohash.org/${id}`} alt="" />
<h2>{name}</h2>
<p>{email}</p>
</div>
}
export default Robot;
- 类式组件
import React from "react";
import Cartcss from "./Cart.module.css"
interface Props{
}
interface State{
isOpen:boolean
}
class Cart extends React.Component<Props, State>{
constructor(props:Props){
super(props);
this.state={
isOpen:false
}
}
render(): React.ReactNode {
return (
<div className={Cartcss.cartContainer}>
<button className={Cartcss.button} onClick={()=>this.setState({isOpen:!this.state.isOpen})}>购物车2(件)</button>
<div className={Cartcss.cartDropDown}
style={
{display: this.state.isOpen ?"block":"none"}}
>
<ul>
<li>robot 1</li>
<li>robot 2</li>
</ul>
</div>
</div>
);
}
}
export default Cart;
5. State和Props
- 简单介绍
state是组件对内的接口,props是组件对外的接口
state是组件内部传递,props是组件间的数据传递
使用setState进行修改state状态
只能在构造函数中进行初始化
- setState是同步还是异步
<button onClick={
()=>{
this.setState({
count:this.state.count+1},()=>{
console.log("count",this.state.count);
})
console.log("count",this.state.count);
}
}>+1</button>
6. react的事件处理
(1)定义函数处理this指向问题
- 箭头函数实现this的指向性
...
class Cart extends React.Component<Props, State>{
constructor(props:Props){
super(props);
this.state={
isOpen:false
}
}
handleClick=()=>{
this.setState({
isOpen:!this.state.isOpen})
}
render(): React.ReactNode {
....
<button className={
Cartcss.button} >购物车2(件)</button>
}
}
export default Cart;
- 函数柯里化
...
class Cart extends React.Component<Props, State>{
constructor(props:Props){
super(props);
this.state={
isOpen:false
}
}
handleClick(){
this.setState({
isOpen:!this.state.isOpen})
}
render(): React.ReactNode {
return (
...
<button className={
Cartcss.button} onClick={
()=>this.handleClick()}>购物车2(件)</button>
...
);
}
}
export default Cart;
(2)event对象
handleClick(e:React.MouseEvent<HTMLButtonElement, MouseEvent>){
console.log("e.target",e.target)
console.log("e.currentTarget",e.currentTarget);
if((e.target as HTMLElement).nodeName==="SPAN")
this.setState({
isOpen:!this.state.isOpen})
}
7. React的生命周期
- 生命周期的简单介绍
constructor(props){
super(props)
console.log('组件将要构造');
}
static getDerivedStateFromProps(props,state){
console.log('组件将要挂载',props,state)
return props
}
getSnapshotBeforeUpdate(){
console.log('组件将要更新');
return '快照'
}
componentDidUpdate(presprops,prestate,snapshotValue){
console.log('更新渲染完成',presprops,prestate,snapshotValue);
}
- 发送请求
componentDidMount(): void {
fetch("http://jsonplaceholder.typicode.com/users").
then(res=>res.json()).
then(data=>this.setState({
robotGallery:data}))
}
- 三个阶段
Mount: 创建虚拟DOM,渲染UI
构建: constructor(){
}
挂载: componentDidMount(){
}
Updata: 更新虚拟DOM,重新渲染UI
处理传递过来的值: static getDerivedStateFromProps(props,state){
return props}
判断是否要更新: shouldComponentUpdate(...): boolean {
}
更新: componentDidUpdate(...) {
}
Unmount: 卸载虚拟DOM,移除UI
卸载: componentWillUnmount() {
}
函数式组件
1. hooks构子
- 简单的介绍
给无状态组件状态(函数式组件)
消息处理的一种方法,用来监视指定程序
函数式组件中处理副作用可以把外部代码钩进来
- 常用钩子
状态钩子:useState 返回值[状态,状态更新函数]
副作用钩子:useEffect
跨组件的状态传递:useContext
全局的状态管理:useReducer
回调的副作用:useCallback
返回引用对象:useRef
2. useState的基本使用
import {
useState} from 'react'
interface Props{
}
const App:React.FC=(props:Props)=> {
const [count,setCount]=useState<number>(1)
return (
<div>
<span>{
count}</span>
<button onClick={
()=>setCount(count+1)}>+1</button>
</div>
)
}
export default App
3. useEffect的基本使用
- 什么是副作用
处理了与函数处理无关的事情
传入相同参数得到了不同的结果
- 副作用钩子的使用(相当于vue的监听器)
import {
useState,useEffect} from 'react'
interface Props{
}
const App:React.FC=(props:Props)=> {
const [count,setCount]=useState<number>(1)
useEffect(()=>{
document.title=`点击了${
count}次`
},[count])
return (
<div>
<span>{
count}</span>
<button onClick={
()=>setCount(count+1)}>+1</button>
</div>
)
}
export default App
- 副作用钩子发送网络请求(第二参数为空数组)
import {
useState,useEffect} from 'react'
interface Props{
}
const App:React.FC=(props:Props)=> {
const [robotGallery,setrobotGallery]=useState<Array<any>>([])
useEffect(()=>{
const fetchData=async ()=>{
const res= await fetch("http://jsonplaceholder.typicode.com/users")
const data= await res.json()
setrobotGallery(data)
}
fetchData()
},[])
return (
<div className={
styles.robotList}>
{
robotGallery.map((item)=><Robot {
...item} key={
item.id}/>)
}
</div>
)
}
export default App
4. useContext的基本使用
(1)React的依赖注入
- 父组件中注入
const defaultContextValue={
author:'qql'
}
export const appContext=React.createContext(defaultContextValue)
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
{
}
<appContext.Provider value={
defaultContextValue}>
<App/>
</appContext.Provider>
</React.StrictMode>
)
- 子组件消费
import React from "react";
import rebot from './robot.module.css'
import {
appContext} from '../../main'
const Robot:React.FC<RobotProps>=()=>{
return <appContext.Consumer>
{
(value)=>
<p>作者:{
value.author}</p>
}
</appContext.Consumer>
}
export default Robot;
(2)使用useContext消费
import React, {
useContext } from "react";
import rebot from "./robot.module.css";
import {
appContext } from "../../main";
const Robot: React.FC<RobotProps> = ({
id, name, email }) => {
const value = useContext(appContext);
return (
<p>作者:{
value.author}</p>
);
};
export default Robot;
(3)使用依赖注入实现全局状态管理
- 定义全局状态管理组件
import React, {
useState } from "react";
interface AppStateValue {
author: string;
shoppingCart: {
items: {
id: number; name: string }[] };
}
interface Props {
children: any;
}
const defaultContextValue: AppStateValue = {
author: "qql",
shoppingCart: {
items: [] },
};
export const appContext = React.createContext(defaultContextValue);
export const appSetStateContext = React.createContext<
React.Dispatch<React.SetStateAction<AppStateValue>> | undefined
>(undefined);
const AppStateProvider: React.FC<Props> = (props) => {
const [state, setState] = useState(defaultContextValue);
return (
<appContext.Provider value={
state}>
{
}
<appSetStateContext.Provider value={
setState}>
{
}
{
props.children}
</appSetStateContext.Provider>
</appContext.Provider>
);
};
export default AppStateProvider;
- 入口组件引入状态管理
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import AppStateProvider from "./Appstate";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
{
}
<AppStateProvider>
<App />
</AppStateProvider>
</React.StrictMode>
);
- 物品组件中消费和修改
import React, {
useContext } from "react";
import rebot from "./robot.module.css";
import {
appContext, appSetStateContext } from "../../Appstate";
interface RobotProps {
id: number;
name: string;
email: string;
}
const Robot: React.FC<RobotProps> = ({
id, name, email }) => {
const value = useContext(appContext);
const setState = useContext(appSetStateContext);
const addCart = (id: number, name: string) => {
console.log(id, name);
if (setState) {
setState((state) => {
return {
...state,
shoppingCart: {
items: [...state.shoppingCart.items, {
id, name }],
},
};
});
}
};
return (
<div className={
rebot.cardContainer}>
<img src={
`https://robohash.org/${
id}`} alt="" />
<h2>{
name}</h2>
<p>{
email}</p>
<p>作者:{
value.author}</p>
<button onClick={
() => addCart(id, name)}>加入购物车</button>
</div>
);
};
export default Robot;
- 购物车中显示
import React from "react";
import Cartcss from "./Cart.module.css";
import {
FiShoppingCart } from "react-icons/fi";
import {
appContext } from "../../Appstate";
interface Props {
}
interface State {
isOpen: boolean;
}
class Cart extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isOpen: false,
};
}
handleClick(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
console.log("e.target", e.target);
console.log("e.currentTarget", e.currentTarget);
if ((e.target as HTMLElement).nodeName === "SPAN")
this.setState({
isOpen: !this.state.isOpen });
}
render(): React.ReactNode {
return (
<appContext.Consumer>
{
(value) => {
return (
<div className={
Cartcss.cartContainer}>
<button
className={
Cartcss.button}
onClick={
(e) => this.handleClick(e)}
>
<FiShoppingCart />
<span>购物车{
value.shoppingCart.items.length}(件)</span>
</button>
<div
className={
Cartcss.cartDropDown}
style={
{
display: this.state.isOpen ? "block" : "none" }}
>
<ul>
{
value.shoppingCart.items.map((item) => {
return (
<li key={
item.id}>
{
item.id}-{
item.name}
</li>
);
})}
</ul>
</div>
</div>
);
}}
</appContext.Consumer>
);
}
}
export default Cart;
5 .自定义Hooks
- 抽取添加购物车的逻辑
import {
useContext } from "react";
import {
appSetStateContext } from "../Appstate";
export const useAddToCart = () => {
const setState = useContext(appSetStateContext);
const addCart = (id: number, name: string) => {
if (setState) {
setState((state) => {
return {
...state,
shoppingCart: {
items: [...state.shoppingCart.items, {
id, name }],
},
};
});
}
};
return addCart;
}
- 使用hooks
import React, {
useContext } from "react";
import rebot from "./robot.module.css";
import {
appContext, appSetStateContext } from "../../Appstate";
import {
useAddToCart } from "../../hooks";
interface RobotProps {
id: number;
name: string;
email: string;
}
const Robot: React.FC<RobotProps> = ({
id, name, email }) => {
const value = useContext(appContext);
const addCart = useAddToCart();
return (
<div className={
rebot.cardContainer}>
<img src={
`https://robohash.org/${
id}`} alt="" />
<h2>打折商品</h2>
<h2>{
name}</h2>
<p>{
email}</p>
<p>作者:{
value.author}</p>
<button onClick={
() => addCart(id, name)}>加入购物车</button>
</div>
);
};
export default Robot;
高阶组件HOC
1. 介绍HOC
const hoc=higherOrde(wrappedComponent)
高阶组件HOC就是一个返回组件的函数
通过组件嵌套的方法给子组件添加更多的功能
接收组件作为参数并返回一个经过改造的新组件
两组件有相同的方法,将方法逻辑封装到HOC中
2. HOC组件的简单应用
- 封装添加购物车的方法
import {
useContext } from "react";
import {
appSetStateContext } from "../../Appstate";
import type {
RobotProps } from "./Robot";
export const withAddToCart = (
ChildComponent: React.ComponentType<RobotProps>
) => {
return (props: RobotProps) => {
const setState = useContext(appSetStateContext);
const addCart = (id: number, name: string) => {
if (setState) {
setState((state) => {
return {
...state,
shoppingCart: {
items: [...state.shoppingCart.items, {
id, name }],
},
};
});
}
};
return <ChildComponent {
...props} addCart={
addCart} />;
};
};
- 使用HOC组件
import React, {
useContext } from "react";
import rebot from "./robot.module.css";
import {
appContext, appSetStateContext } from "../../Appstate";
import {
withAddToCart } from "./AddToCart";
export interface RobotProps {
id: number;
name: string;
email: string;
addCart: (id: number, name: string) => void;
}
const Robot: React.FC<RobotProps> = ({
id, name, email, addCart }) => {
const value = useContext(appContext);
return (
<div className={
rebot.cardContainer}>
<img src={
`https://robohash.org/${
id}`} alt="" />
<h2>{
name}</h2>
<p>{
email}</p>
<p>作者:{
value.author}</p>
<button onClick={
() => addCart(id, name)}>加入购物车</button>
</div>
);
};
export default withAddToCart(Robot);
组件之间的通讯
1. props父传子
- 子组件
import React from "react";
interface Props {
title: string;
}
export const A: React.FC<Props> = (props) => {
return <div>{
props.title}</div>;
};
A.defaultProps = {
title: '你好' }
- 父组件
import {
A } from "./A";
function App() {
return (
<A title={
"我的值来自父组件"} />
);
}
export default App;
2. props子传父(父向子传递一个函数)
- 子组件
import React from "react";
interface Props {
getChildProps: Function;
}
export const A: React.FC<Props> = (props) => {
return (
<div>
<button onClick={
() => props.getChildProps(10)}>子传父</button>
</div>
);
};
- 父组件
import {
A } from "./A";
import {
useState } from "react";
function App() {
const [count, setCount] = useState(0);
const getChildProps = (value: any) => {
setCount(value);
};
return (
<>
<A getChildProps={
getChildProps} />
<div>{
count}</div>
</>
);
}
export default App;
3. props插槽(props传递一个jsx)
(1)默认插槽(无需传)
- 父组件
import {
A } from "./A";
function App() {
return (
<A>
<span>默认插槽</span>
</A>
);
}
export default App;
- 子组件
import React from "react";
interface Props {
children: JSX.Element;
}
export const A: React.FC<Props> = (props) => {
return <div>{
props.children}</div>;
};
(2)具名插槽(props传)
- 父组件
import {
A } from "./A";
function App() {
return (
<A title={
<span>具名插槽</span>}></A>
);
}
export default App;
- 子组件
import React from "react";
interface Props {
title: JSX.Element;
}
export const A: React.FC<Props> = (props) => {
return <div>{
props.title}</div>;
};
4. 跨级传输
(1)简单的使用
- 父组件
import {
A } from "./A";
import React from "react";
const defaultContextValue = {
title: "",
};
export const appContext = React.createContext(defaultContextValue);
function App() {
defaultContextValue.title = "跨级传输";
return (
<appContext.Provider value={
defaultContextValue}>
<A></A>
</appContext.Provider>
);
}
export default App;
- 子(后代)组件
import React, {
useContext } from "react";
import {
appContext } from "./App";
interface Props {
}
export const A: React.FC<Props> = (props) => {
const res = useContext(appContext);
return (
<appContext.Consumer>
{
(value) => (
<div>
{
value.title}-{
res.title}
</div>
)}
</appContext.Consumer>
);
};
(2)模块化传输
- 传输模块
import React from "react";
interface Props<T> {
children: any;
value: T;
}
export const appContext: React.Context<{
title: string } | {
}> =
React.createContext({
});
export const ProviderConsumer: React.FC<Props<{
title: string }>> = (props) => {
return (
<appContext.Provider value={
props.value}>
{
props.children}
</appContext.Provider>
);
};
- 父组件
import {
A } from "./A";
import {
ProviderConsumer } from "./ProviderConsumer";
function App() {
return (
<ProviderConsumer value={
{
title: "夸级传输" }}>
<A></A>
</ProviderConsumer>
);
}
export default App;
- 子(后代)组件
import React, {
useContext } from "react";
import {
appContext } from "./ProviderConsumer";
interface Props {
}
export const A: React.FC<Props> = (props) => {
const value = useContext(appContext) as any;
return <div>{
value.title}</div>;
};