react hook
hooks的目的是为了给函数式组件加上状态;
hooks 一律使用use作为前缀命名,常用钩子useState,useEffect,useContext(跨组件数据传递),useReducer(管理全局状态)useCallback(处理回调副作用),useRef(返回引用对象,不变)
1.useContext 跨组件传递数据
createContext useContext
一级组件创建:React.createContext
**index.tsx**
const defaultContextValue = {
username: "阿莱克斯",
};
export const appContext = React.createContext(defaultContextValue);
** child.tsx**
二级三级消费:
import React, { useContext } from "react";
import { appContext } from "../index";
const value = useContext(appContext);
{value.username}
或者Provider,Consumer
一级组件创建:React.createContext
**index.tsx**
const defaultContextValue = {
username: "阿莱克斯",
};
export const appContext = React.createContext(defaultContextValue);
<appContext.Provider value={ defaultContextValue }>
<App />
</appContext.Provider>
** child.tsx**
二级三级消费:
import React, { useContext } from "react";
import { appContext } from "../index";
const value = useContext(appContext);
{value.username}
<appContext.Consumer>
{(value) => {
return <li>
<p>{value.username}</p>
</li>
}}
</appContext.Consumer>
升级:全局context
新建appState.tsx. 导出AppStateProvider组件
appState.tsx
import React, { useState } from "react";
interface AppStateValue {
username: string;
shoppingCart: { items: { id: number; name: string }[] };
}
const defaultContextValue: AppStateValue = {
username: "阿莱克斯",
shoppingCart: { items: [] },
};
export const appContext = React.createContext(defaultContextValue);
export const appSetStateContext = React.createContext<
React.Dispatch<React.SetStateAction<AppStateValue>> | undefined
>(undefined);
//最外层使用
export const AppStateProvider: React.FC = (props) => {
const [state, setState] = useState(defaultContextValue);
return (
//相当于一个高阶的HOC
<appContext.Provider value={state}>
<appSetStateContext.Provider value={setState}>
{props.children}
</appSetStateContext.Provider>
</appContext.Provider>
);
};
index.tsc
最外层AppStateProvider包裹
import { AppStateProvider } from "./AppState";
ReactDOM.render(
<React.StrictMode>
<AppStateProvider>
<App />
</AppStateProvider>
</React.StrictMode>,
document.getElementById("root")
);
二级组件消费
import React, { useContext } from "react";
import styles 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 addToCart = () => {
if(setState) { // 思考: 同学们可以想一想如何化简这里的代码
setState(state => {
return {
...state,
shoppingCart: {
items: [...state.shoppingCart.items, {id, name}]
}
}
})
}
}
return (
<div className={styles.cardContainer}>
<img alt="robot" src={`https://robohash.org/${id}`} />
<h2>{name}</h2>
<p>{email}</p>
<p>作者:{value.username}</p>
<button onClick={addToCart}>加入购物车</button>
</div>
);
};
export default Robot;
2.useEffect 副作用
1.与药物的副作用类似:减肥药(拉肚子),头孢(过敏);
2.纯函数
(1).给一个函数同样的参数,那么这个函数永远返回同样的
(2).函数式编程理念
3.副作用与纯函数相反,指一个函数处理了与返回值无关的事情;
y=f(x)纯函数
椭圆,一个x对应多个y,不确定的情况就是副作用;
副作用是坏事吗?
很多代码必须的借助副作用: AJAX,修改dom,甚至是console.log;
React:state状态的变化,生命周期,构建函数
3.useMemo
假设我们有一个计算函数,该函数需要大量时间才能得出结果,我们可以使用 useMemo 来缓存该函数的结果,以避免在每次渲染时都重新计算。
import { useMemo } from 'react';
function expensiveCalculation(number) {
console.log('calculating...');
let result = 0;
for (let i = 0; i < number; i++) {
result += i;
}
return result;
}
function App() {
const [inputValue, setInputValue] = useState(0);
const result = useMemo(() => expensiveCalculation(inputValue), [inputValue]);
return (
<div>
<label>
Input value:
<input type="number" onChange={(e) => setInputValue(e.target.value)} />
</label>
<p>Result: {result}</p>
</div>
);
}
在这个例子中,我们定义了一个昂贵的计算函数 expensiveCalculation,然后在组件中使用 useMemo 来缓存其结果。每次 inputValue 更改时,useMemo 会检查依赖项数组 [inputValue] 是否更改,如果更改,它会重新计算 expensiveCalculation 的结果。否则,它将返回先前缓存的值。
自变量
{useState,useReducer,useContext}
因变量
{useMemo,useEffect,useCallback}
其他
useRef
import {useState} from ‘react’
function Count({data}){//data就相当于props
return <i>{data}</i>
}
export default function App(){
const [x,setX]=useState(0);
const y=2*x+1;
const changeX=()=>setX(x+1);
const renderCountRef = useRef(1);
const isOdd =renderCountRef.current%2!=0;
renderCountRef.current++;
//const changeX=useCallback(()=>setX(x+1),[x])
return(<ul onClick={changeX}>
<li>x是{x}</li>
<li>y是{y}</li>
<li>x是<Count data={x}/></li>
<li>y是<Count data={y}/></li>
{isOdd?<li>x是{x}</li>:null}//奇数次更新的时候显示
</ul>)
x变化y变化
}
1.自变量==>视图的变化
useState将X的值渲染为li标签的值,定义changX方法,将changX绑定为ul的点击回调函数
2.因变量==>视图 的变化
useMemo,useCallback在遇到性能瓶颈之前都可以不使用;
使用好处;y与changeX会缓存下来,只要x不变,读取的是缓存的值,
不使用时会基于x创建新的y与changX
3.副作用===>自变量导致因变量变化后,因变量不仅可以改变视图,还可以触发副作用
useEffect
请求数据,操作Dom
4.useReducer相当于进阶版的useState
多个state 吧x,y作为props传入
5.useContext
一级组件创建createContext之后,可以在后面任意二级,三级。。。直接使用useContext
6.useRef
自变量与因变量,因变量与视图,自变量与视图
其他
HOC higherOrde 高阶函数组件
1.返回了组件的函数;
2.通过组件嵌套的方法给子组件添加跟多功能
3.接收一个组件作为参数并返回一个经过改造的新组件
AddToCart.tsx
import React, { useContext } from "react";
import { appSetStateContext } from "../AppState";
import { RobotProps } from "./Robot";
export const withAddToCart = (ChildComponent: React.ComponentType<RobotProps>) => {
return (props) => {
const setState = useContext(appSetStateContext)
const addToCart = (id, name) => {
if (setState) {
// 思考: 同学们可以想一想如何化简这里的代码
setState((state) => {
return {
...state,
shoppingCart: {
items: [...state.shoppingCart.items, { id, name }],
},
};
});
}
}
return <ChildComponent {...props} addToCart={addToCart} />
};
}
Robot.tsx
消费useContext
import React, { useContext } from "react";
import styles from "./Robot.module.css";
import { appContext, appSetStateContext } from "../AppState";
import { withAddToCart } from './AddToCart';
export interface RobotProps {
id: number;
name: string;
email: string;
addToCart: (id, name) => void;
}
const Robot: React.FC<RobotProps> = ({ id, name, email, addToCart }) => {
const value = useContext(appContext);
return (
<div className={styles.cardContainer}>
<img alt="robot" src={`https://robohash.org/${id}`} />
<h2>{name}</h2>
<p>{email}</p>
<p>作者:{value.username}</p>
<button onClick={() => addToCart(id, name)}>加入购物车</button>
</div>
);
};
export default withAddToCart(Robot);
appState.tsx
导出AppStateProvider组件 创建
import React, { useState } from 'react'
interface AppStateValue {
username: string;
shoppingCart: { items: { id: number; name: string }[] };
}
const defaultContextValue: AppStateValue = {
username: "阿莱克斯",
shoppingCart: { items: [] },
};
export const appContext = React.createContext(defaultContextValue);
export const appSetStateContext = React.createContext<
React.Dispatch<React.SetStateAction<AppStateValue>> | undefined
>(undefined);
export const AppStateProvider: React.FC = (props) => {
const [state, setState] = useState(defaultContextValue);
return (
<appContext.Provider value={state}>
<appSetStateContext.Provider value={setState}>
{props.children}
</appSetStateContext.Provider>
</appContext.Provider>
);
}
自定义hook use***
**addToCart.tsx***
export const useAddToCart = () => {
const setState = useContext(appSetStateContext)
const addToCart = (id, name) => {
if (setState) {
// 思考: 同学们可以想一想如何化简这里的代码
setState((state) => {
return {
...state,
shoppingCart: {
items: [...state.shoppingCart.items, { id, name }],
},
};
});
}
}
return addToCart;
}
***RobotDiscount.tsx**
import { useAddToCart } from "./AddToCart";
interface RobotProps {
id: number;
name: string;
email: string;
}
const RobotDiscount: React.FC<RobotProps> = ({ id, name, email }) => {
const value = useContext(appContext);
const addToCart = useAddToCart();
return (
<div className={styles.cardContainer}>
<img alt="robot" src={`https://robohash.org/${id}`} />
<h2>打折商品</h2>
<h2>{name}</h2>
<p>{email}</p>
<p>作者:{value.username}</p>
<button onClick={() => addToCart(id, name)}>加入购物车</button>
</div>
);
};
export default RobotDiscount;