基础
所有react组件都要用大写命名,跟原生区分。
1,变量
//useState 给组件里面添加状态变量的
//状态变量和js普通变量的区别:状态变量会引起视图的变化,相当于双向绑定
import { useState } from "react";
function App() {
const [num,setNum]=useState(0)
//num 状态变量
//setNum 修改状态变量的方法
const clickHandler=()=>{
setNum(num+1)
}
return (
<div className="App">
<button onClick={clickHandler}>{num}</button>
</div>
);
}
export default App;
2,修改对象
//useState 给组件里面添加状态变量的
//状态变量和js普通变量的区别:状态变量会引起视图的变化,相当于双向绑定
import { useState } from "react";
function App() {
const [form,setForm]=useState({name:'daliy'})
//num 状态变量
//setNum 修改状态变量的方法
const clickHandler=()=>{
setForm({
...form,
name:'wow'
})
}
return (
<div className="App">
<div>{form.name}</div>
<button onClick={clickHandler}>点击修改名字</button>
</div>
);
}
export default App;
3,控制显示,类似v-if
{user.uid===item.user.uid && <span className="delete-btn" onClick={()=>handleDel(item.rpid)}>删除</span>}
4,调试工具,react dev tools
5,v-if判断
className={`nav-item ${type===item.type?'active':''}`}
//等于
className={`nav-item ${type===item.type && 'active'}`}
6,操作dom
import { useRef } from "react";
const App = () => {
//useRef生产ref对象,绑定到dom标签身上
const inputRef=useRef(null)
//dom可用时候,ref.current获取dom
//渲染完毕之后dom生成之后才可以使用
const showDom=()=>{
console.log(inputRef.current);
}
return (
<div>
<input type="text" ref={inputRef}></input>
<button onClick={showDom}>获取dom</button>
</div>
);
};
export default App;
组件传参
1,父传子
props可以传递任何数据(jsx也可以)
props是只读对象,只有父组件可以修改props
//父传子
function APP(){
const msg="父传子"
return(
<div>father
<div><Son msg={msg}></Son></div>
</div>
)
}
export default APP
function Son(props){
return(
<div>this is Son
<div>{props.msg}</div>
</div>
)
}
传jsx
//父传子
function APP(){
return(
<div>father
<div><Son jsx={<span>父组件jsx</span>}></Son></div>
</div>
)
}
export default APP
function Son(props){
return(
<div>this is Son
<div>{props.jsx}</div>
</div>
)
}
children属性(类似插槽)
//children
function APP(){
return(
<div>father
<Son>
{/* 父组件不会出现 */}
<span>父span</span>
</Son>
</div>
)
}
export default APP
function Son(props){
return(
<div>
{props.children}
</div>
)
}
2,子传父
在子组件内调用父组件的函数并传递参数
//子传父:在子组件内调用父组件的函数并传递参数
function APP() {
const getMsg = (msg) => {
console.log(msg);
};
return (
<div>
<Son OnGetMsg={getMsg}></Son>
</div>
);
}
export default APP;
function Son(props) {
return (
<div>
<button onClick={() => props.OnGetMsg("111")}>点击子传父</button>
</div>
);
}
3,兄弟传值
使用状态提升
import { useState } from "react";
//兄弟组件传信
function APP() {
const [msg,setMsg]=useState('')
const getMsg = (msg) => {
console.log(msg);
setMsg(msg)
};
return (
<div>
<A OnGetMsg={getMsg}></A>
<B msg={msg}></B>
</div>
);
}
export default APP;
function A({OnGetMsg}) {
return (
<div>
<button onClick={()=>OnGetMsg("传给B的数据")}>点击传给B数据</button>
</div>
);
}
function B({msg}) {
return (
<div>
<div>{msg}</div>
</div>
);
}
4,多层级传值
1,用creatContext创建对象
2,在最顶层用创建出来的对象的Provider属性的value属性注入值
3,在要使用的地方用useContext使用
import { createContext, useContext } from "react";
const MsgContext = createContext(); //首字母大写一般
function A() {
return (
<div>
<B></B>
</div>
);
}
function B() {
const msg = useContext(MsgContext);
return <div>{msg}</div>;
}
//跨层传递数据,主要用Content
function APP() {
const msg="app的数据"
return (
<div>
<MsgContext.Provider value={msg}>
<A></A>
</MsgContext.Provider>
</div>
);
}
export default APP;
useEffect
useEffect,reace hook函数,创建React组件中不是由事件引起,由于渲染本身引起的操作,比如发生AJAX请求,更改DOM等。比如渲染完之后就要发请求拿数据
1,参数1是一个函数,可以把他叫做副作用函数,在函数内部可以放置要执行的操作
2,参数2是一个数组(可选参),在数组防止依赖项,不同的依赖项会影响第一个参数函数的执行。当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
没有依赖项:组件初始渲染+组件更新时执行
空数组依赖:只在初始渲染时执行一次
添加特定依赖项:组件初始渲染,特性依赖项变化时执行
例子:在组件渲染完毕之后,立刻从服务端获取平道列表数据并显示到页面中
//useEffect,reace hook函数,创建React组件中不是由事件引起,由于渲染本身引起的操作,比如发生AJAX请求,更改DOM等。比如渲染完之后就要发请求拿数据
import { useEffect, useState } from "react";
const URL = "http://geek.itheima.net/v1_0/channels";
function APP() {
const [list, setList] = useState([]);
useEffect(() => {
//需要完成的操作
async function getList() {
const res = await fetch(URL);
const jsonRes = await res.json();
console.log(jsonRes);
setList(jsonRes.data.channels);
}
getList();
}, []);
return (
<div>
this is app
<div>
{list.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
</div>
);
}
export default APP;
不同依赖项说明
//依赖项不同,useEffect执行也会不同,
import { useEffect, useState } from "react";
//1,依赖项为空,初始渲染完成+组件更新会执行
// function APP() {
// useEffect(()=>{
// console.log('useEffect执行');
// })
//2,依赖项为空数组,只会初始渲染执行一次
// function APP() {
// useEffect(()=>{
// console.log('useEffect执行');
// },[])
//3,传入指定依赖项,初始+依赖项发生变化
function APP() {
const [count,setCount]=useState(0)
useEffect(()=>{
console.log('useEffect执行');
},[count])
return (
<div>
<div>{count}</div>
<button onClick={()=>setCount(count+1)}>加</button>
</div>
);
}
export default APP;
清除副作用
在useEffect中编写的由渲染本身引起的对接组件外部的操作,就经常叫做副作用操作,比如在useEffect中开启了一个定时器,我们想组件卸载时把定时器清除掉,这个过程就是清楚副作用操作
//清除副作用
import { useEffect, useState } from "react";
function A() {
useEffect(() => {
const timer = setInterval(() => {
console.log("在执行");
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return <div>A组件</div>;
}
function APP() {
const [show, setShow] = useState(true);
return (
<div>
{show && <A></A>}
<button onClick={() => setShow(false)}>卸载组件</button>
</div>
);
}
export default APP;
自定义hook函数
//自定义useShow函数
import { useState } from "react";
//1,定义函数
function useShow() {
//2,抽离可复用的逻辑代码
const [show, setShow] = useState(true);
const handleShow = () => {
setShow(!show);
};
//3,return暴露出去(对象或者数组)
return {
show,
handleShow,
};
}
function APP() {
//4,使用useShow自定义hook
const { show, handleShow } = useShow();
return (
<div>
{show && <div>显示内容</div>}
<button onClick={handleShow}>更改显示</button>
</div>
);
}
export default APP;
使用规则
1,只能在组件中或者其他自定义hook函数中调用
2,只能在组件顶层调用,不能嵌套在if,for,其他函数中
Redux状态管理
state:一个对象,存放着我们管理的数据状态
action:一个对象,用来描述你想怎么修改数据
包含两个属性:type:标识属性,值为字符串,唯一必要属性
data:数据属性,可选
reducer:一个函数,根据action的描述生成一个新的state,可以初始化状态,修改状态
在react中使用redux,需要两个插件:Redux Toolkit和react-redux
Retux toolkit:简化书写方式
react-redux:链接react和redux的中间件
使用过程:1,创建store子模块
import { createSlice } from "@reduxjs/toolkit";
const counterStore=createSlice({
name:'counter',
//初始化状态
initialState:{
count:0
},
//修改数据方法函数,同步执行,支持直接修改
reducers:{
inscrement(state){
state.count++
},
decrement(state){
state.count--
},
//action传参到action.payload属性上
addToNum(state,action){
state.count=action.payload
}
}
})
//解构actionCreater函数
//上面的写法是通过reduxjs/toolkit简化的,所以这里是从actions拿去
const {inscrement,decrement,addToNum}=counterStore.actions
//获取reducer
const reducer=counterStore.reducer
导出actionCreater 修改store数据的唯一方法
export {inscrement,decrement,addToNum}
export default reducer
2,在store文件中的index下集成
import { configureStore } from "@reduxjs/toolkit";
//导入子模块的reducer
import counterReducer from './modules/counterStore'
const store=configureStore({
reducer:{
counter:counterReducer
}
})
export default store
3,在项目的根index下用Provider注入store
import { createRoot } from 'react-dom/client'
import App from './App'
import store from './store'
import { Provider } from 'react-redux'
const root = createRoot(document.querySelector('#root'))
root.render(
<Provider store={store}>
<App></App>
</Provider>
)
4,使用和修改store
import { useDispatch, useSelector } from "react-redux";
import {inscrement,decrement,addToNum} from "./store/modules/counterStore"
function APP() {
//读取sotre数据状态
const { count } = useSelector((state) => state.counter);
//修改store数据
const dispatch = useDispatch();
return (
<div>
{/* 执行dispatch修改数据 */}
<button onClick={()=>dispatch(inscrement())}>+</button>
<div>{count}</div>
<button onClick={()=>dispatch(decrement())}>-</button>
{/* dispatch带参数的情况 */}
<button onClick={()=>dispatch(addToNum(10))}>to10</button>
</div>
);
}
export default APP;
redux异步处理
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const channelStore = createSlice({
name: "channelList",
initialState: {
channelList: [],
},
reducers: {
setChannels(state, action) {
state.channelList = action.payload;
},
},
});
const { setChannels } = channelStore.actions;
//异步请求部分,单独创建个异步请求函数
const getChannelList = () => {
return async (dispatch) => {
const res = await axios.get("http://geek.itheima.net/v1_0/channels");
dispatch(setChannels(res.data.data.channels));
};
};
export { getChannelList };
const reducer = channelStore.reducer;
export default reducer;
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getChannelList } from "./store/modules/channelStore";
function APP() {
const { channelList } = useSelector((state) => state.channel);
const dispatch = useDispatch();
//使用useEffect触发异步请求获取数据
useEffect(() => {
dispatch(getChannelList());
}, [dispatch]);
return (
<div>
<div>
{channelList.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
</div>
);
}
export default APP;
redux和useState关系
拓展,zustand,creatAsyncThunk
router路由
createBrowserRouter, RouterProvider
import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
const router = createBrowserRouter([
{ path: "/login", element: <div>我是登录页</div> },
{ path: "/article", element: <div>我是文章页</div> },
]);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<RouterProvider router={router}>
</RouterProvider>
</React.StrictMode>
);
路由跳转
1,Link
import { Link } from "react-router-dom";
const Login = () => {
return (
<div>
我是登录页
<Link to="/article">去文章页</Link>
</div>
);
};
export default Login;
2,useNavigate
import { useNavigate } from "react-router-dom"
const Article =()=>{
const navigate=useNavigate()
return (
<div>我是文章页
<button onClick={()=>navigate('/login')}>去登录页</button>
</div>
)
}
export default Article
3,路由传值
3.1,useSearchParams
传
import { useNavigate } from "react-router-dom"
const Article =()=>{
const navigate=useNavigate()
return (
<div>我是文章页
<button onClick={()=>navigate('/login?id=100&name=jack')}>传参去登录页</button>
</div>
)
}
export default Article
接
import { useSearchParams } from "react-router-dom";
const Login = () => {
const [params]=useSearchParams()
const id=params.get('id')
const name=params.get('name')
return (
<div>
我是登录页{id}-{name}
</div>
);
};
export default Login;
3.2 params
传
<button onClick={()=>navigate('/login/1001/jack')}>传参去登录页</button>
路由配置
import Login from "../page/Login";
import Article from "../page/Article";
import { createBrowserRouter } from "react-router-dom";
const router =createBrowserRouter([
{
path:'/login/:id/:name',
element:<Login/>
},
{
path:'/article/',
element:<Article/>
},
])
export default router
login取
import { useParams } from "react-router-dom";
const Login = () => {
const params = useParams();
const id = params.id;
const name =params.name;
return (
<div>
我是登录页{id}-{name}
</div>
);
};
export default Login;
4,路由嵌套
import { Link, Outlet } from "react-router-dom";
const Layout = () => {
return (
<div>我是一级路由
<div>
<Link to='./about'>关于</Link>
<Link to='./board'>面板</Link>
<Outlet/>
</div>
</div>
)
};
export default Layout;
import { createBrowserRouter } from "react-router-dom";
import Layout from "../page/Layout";
import About from "../page/About";
import Board from "../page/Board";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
path: "about",
element: <About />,
},
{
path: "board",
element: <Board />,
},
],
},
]);
export default router;
children
设置默认路由
children: [
{
//设置为默认路由
// path: "about",
index:true,
element: <About />,
},
{
path: "board",
element: <Board />,
},
],
import { Link, Outlet } from "react-router-dom";
const Layout = () => {
return (
<div>我是一级路由
<div>
<Link to='/'>关于</Link>
<Link to='/board'>面板</Link>
<Outlet/>
</div>
</div>
)
};
export default Layout;
404配置
*
{
path: "*",
element: <NotFound />,
}
路由模式 hash,history
useReducer
与useState的作用类似,用来管理相对复杂的数据
通过dispatch函数分派一个action对象(type和payload),通知reducer要返回哪个状态并渲染UI
import { useReducer } from "react";
function reducer(state, action) {
//根据传入的action返回新的state
switch (action.type) {
case "Add":
return state + 1;
//传参
case "Set":
return action.payload;
default:
return state;
}
}
const Home = () => {
const [state, dispatCh] = useReducer(reducer, 0);
return (
<div>
<div>{state}</div>
<button onClick={() => dispatCh({ type: "Add" })}>+</button>
<button onClick={() => dispatCh({ type: "Set",payload:100 })}>设置100</button>
</div>
);
};
export default Home;
useMemo
作用:在组件每次重新渲染的时候缓存计算的结果,只有依赖发生变化才会执行
(react任一组件变化,所在组件都会刷新,包括函数调用,定义变量等等)
useMemo(()=>{
//只有当res发生变化时,这里面的函数才会执行,否则永远都是缓存之前的结果
},[res])
React.memo
允许组件在props没有发生改变的时候,跳过重新渲染。
react组件默认的渲染机制:只要父组件重新渲染了子组件也会重新渲染
const MemoComponent=memo(function xx(props){
//..
})
在使用memo缓存组件后,react会对每个prop使用Object.is来比较新值旧值,为true就不渲染
基本数据类型会返回true,复杂类型只关心引用是否改变。当父组件重新渲染时,形成的是新的数组引用,可以和useMemo结合使用,保证prop引用稳定
useCallback
使用useCallback包裹函数后,函数可以保证在组件渲染的时候保持引用稳定。
forwardRef
父组件通过useRef获取子组件内部的ref元素
import { forwardRef, useRef } from "react";
const Son = forwardRef((props, ref) => {
return (
<div>
<div ref={ref}></div>
</div>
);
});
function Father() {
const sonRef = useRef(null);
return (
<div>
<Son ref={sonRef} />
</div>
);
}
export default Father;
useImperativeHandle
通过ref暴露子组件的方法给父组件调用,和forwardRef一起配合使用
import { forwardRef, useImperativeHandle, useRef } from "react";
const Son = forwardRef((props, ref) => {
const add = () => {
console.log(111);
};
useImperativeHandle(ref, () => {
return {
add,
};
});
return (
<div>
<div ref={ref}></div>
</div>
);
});
function Father() {
const sonRef = useRef(null);
function handel(){
console.log(sonRef.current);
}
return (
<div>
<Son ref={sonRef} />
<button onClick={handel}>22</button>
</div>
);
}
export default Father;
class类组件
类似vue2 options api
通过类属性state定义状态数据 2. 通过setState方法来修改状态数据 3. 通过render来写UI模版(JSX语法一致)
import { Component } from "react";
class Counter extends Component{
//1,状态变量,2,事件回调 3,UI(jsx)
//1,状态变量
state={
count:0
}
//事件
setCount=()=>{
//修改状态变量
this.setState({
count:this.state.count+1
})
}
render(){
return <button onClick={this.setCount}>{this.state.count}</button>
}
}
export default Counter
类组件生命周期
componentDidMount:组件挂载完毕自动执行 - 异步数据获取
componentWillUnmount: 组件卸载时自动执行 - 清理副作用
组件通信
1. 父传子:通过prop绑定数据
2. 子传父:通过prop绑定父组件中的函数,子组件调用
3. 兄弟通信:状态提升,通过父组件做桥接
zustand
极简的状态管理工具
import { useEffect } from "react";
import { create } from "zustand";
const useStore = create((set) => {
return {
//状态数据
count: 0,
//修改数据方法
add: () => {
set((state) => ({ count: state.count + 1 }));
},
//异步处理
list:[],
getList:async()=>{
const res=await ...
set({list:res.data})
}
};
});
function App() {
//组件使用
const { count, add,list,getList } = useStore();
useEffect(()=>{
getList()
},[])
return (
<div>
<button onClick={add}>{count}</button>
</div>
);
}
export default App;
切片模式
import { create } from "zustand";
//zustand切片模式
const createCounterStore = ((set) => {
return {
count: 0,
add: () => {
set((state) => ({ count: state.count + 1 }));
},
};
});
const createListStore = ((set) => {
return {
list: [],
setList: () => {
set({list:[1,2]});
},
};
});
const useStore = create((...a) =>{
return{
...createCounterStore(...a),
...createListStore(...a)
}
})