浅学react

基础

所有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)
  }
  
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值