React笔记-第二篇

Redux · 语雀

1.Rdux集中状态管理工具

<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>

<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>

<script>
  // 1. 定义reducer函数 
  // 作用: 根据不同的action对象,返回不同的新的state
  // state: 管理的数据初始状态
  // action: 对象 type 标记当前想要做什么样的修改
  function reducer (state = { count: 0 }, action) {
    // 数据不可变:基于原始状态生成一个新的状态
    if (action.type === 'INCREMENT') {
      return { count: state.count + 1 }
    }
    if (action.type === 'DECREMENT') {
      return { count: state.count - 1 }
    }
    return state
  }

  // 2. 使用reducer函数生成store实例
  const store = Redux.createStore(reducer)

  // 3. 通过store实例的subscribe订阅数据变化
  // 回调函数可以在每次state发生变化的时候自动执行
  store.subscribe(() => {
    console.log('state变化了', store.getState())
    document.getElementById('count').innerText = store.getState().count
  })

  // 4. 通过store实例的dispatch函数提交action更改状态 
  const inBtn = document.getElementById('increment')
  inBtn.addEventListener('click', () => {
    // 增
    store.dispatch({
      type: 'INCREMENT'
    })
  })

  const dBtn = document.getElementById('decrement')
  dBtn.addEventListener('click', () => {
    // 减
    store.dispatch({
      type: 'DECREMENT'
    })
  })

  // 5. 通过store实例的getState方法获取最新状态更新到视图中

</script>

 

2. Reduc与React-环境准备

 

图文并茂五分钟搞懂react中的reducer 

 counterStore.js文件

// 引入toolkit中的creatSlice方法

import { createSlice } from "@reduxjs/toolkit"

// reducer切片
// createSlice函数的作用:生成分片的reducer
// 内部调用的市createAction和createReducer 
// creatSlice可以帮助我们用更少的代码去生成配套的reducer和action,而且有很好的维护性
const counterStore = createSlice({
    name: 'counter',
    // 初始化state
    initialState: {
        count: 0
    },
    // 修改状态的方法 同步方法 支持直接修改
    reducers: {
        // 指定state的各种操作,直接就可以在对象中添加方法
        add(state) {
            // 可以通过不同的方法修来指定对state的不同的操作
            // state,action 
            // 可以直接修改state => state 是一个代理对象  之前的复制一份,返回一个新的对象
            state.count++
        },
        reduce(state) {
            state.count--
        }
    }
})
// action解构出来actionCreater函数
const { add, reduce } = counterStore.actions
// 获取reducer
const reducer = counterStore.reducer
// 以按需导出的方式导出actionCreater
export { add, reduce }
// 以默认导出的方式导出reducer
export default reducer

store/index.js文件 

import { configureStore } from "@reduxjs/toolkit";
import counterStore from "./modules/counterStore";
const store = configureStore({
    reducer: {
        counter: counterStore
    }
})
export default store

src/index.js

// 项目的入口,从这里开始运行
// React必要的两个核心包
import React from 'react';
import ReactDOM from 'react-dom/client';

// 导入项目的根组件
import App from './App';
import store from './store';
import { Provider } from 'react-redux';

// 把App根组件渲染到id为root的dom节点上
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
        <App />
    </Provider>
);

 

import { useSelector } from "react-redux";
function App() {
  const { count } = useSelector(state => state.counter)
  return (
    <div className="App">
      {count}
    </div>
  );
}

export default App;

import { useSelector, useDispatch } from "react-redux";
import  { add, reduce } from "./store/modules/counterStore"
function App() {
  const { count } = useSelector(state => state.counter)
  const dispatch = useDispatch()
  return (
    <div className="App">
      <button onClick={()=>dispatch(add())}>+</button>
      {count}
      <button onClick={()=>dispatch(reduce())}>-</button>

    </div>
  );
}

export default App;

 

 2.1Redux与React-提交action传参

2.2Redux与React-异步状态操作

channelStore.js文件

// 导入必要的模块
import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'

// 使用 createSlice 创建 Redux 切片
const channelStore = createSlice({
  name: 'channel',
  initialState: {
    channelList: []
  },
  reducers: {
    // 定义 reducer 函数,用于更新 channelList
    setChannelList(state, action) {
      state.channelList = action.payload
    }
  }
})

// 从切片中导出 actionCreators 中的 setChannelList
const { setChannelList } = channelStore.actions

// 定义 API 地址
const url = 'http://geek.itheima.net/v1_0/channels'

// 封装异步 actionCreator 函数,用于获取频道列表并更新状态
const fetchChannelList = () => {
  return async (dispatch) => {
    // 发起异步请求获取频道列表数据
    const res = await axios.get(url)
    
    // 使用 dispatch 触发 setChannelList action,将获取的数据作为 payload 传递
    dispatch(setChannelList(res.data.data.channels))
  }
}

// 导出异步 actionCreator 函数 fetchChannelList
export { fetchChannelList }

// 从切片中导出 reducer 函数
const channelReducer = channelStore.reducer
export default channelReducer

 index.js文件

import { configureStore } from "@reduxjs/toolkit";
import counterStore from "./modules/counterStore";
import channelReducer from "./modules/channelStore";
const store = configureStore({
    reducer: {
        counter: counterStore,
        channel:channelReducer
    }
})
export default store

App.js

// 导入 React 相关模块
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'

// 导入异步 actionCreator 函数 fetchChannelList
import { fetchChannelList } from './store/channelStore'

// 定义 App 组件
function App() {
  // 使用 useSelector 从 Redux store 中获取频道列表数据
  const { channelList } = useSelector(state => state.channel)

  // 使用 useDispatch 获取 dispatch 函数
  const dispatch = useDispatch()

  // 使用 useEffect 钩子,在组件加载时触发异步请求获取频道列表数据
  useEffect(() => {
    // 调用异步 actionCreator 函数 fetchChannelList,触发数据请求和状态更新
    dispatch(fetchChannelList())
  }, [dispatch])

  // 渲染组件
  return (
    <div className="App">
      {/* 列出频道列表数据 */}
      <ul>
        {channelList.map(task => <li key={task.id}>{task.name}</li>)}
      </ul>
    </div>
  )
}

// 导出 App 组件
export default App

 2.3Redux调试-devtools

3.美团案例-环境准备

3.1美团案例-分类和商品列表渲染

启动后端服务

启动前端服务

4. ReactRouter路由

// 项目的入口,从这里开始运行
// React必要的两个核心包
import React from 'react';
import ReactDOM from 'react-dom/client';

// 导入项目的根组件
import App from './App';
import store from './store';
import { Provider } from 'react-redux';

//导入react-router
import { createBrowserRouter,RouterProvider } from 'react-router-dom';

//创建router实例对象并且配置路由对应关系
const router=createBrowserRouter([
    {
        path:'/login',
        element:<div>我是登录</div>
    },
    {
        path:'/personal',
        element:<div>我是个人</div>
    }
])

// 把App根组件渲染到id为root的dom节点上
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router}></RouterProvider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals

4.1抽象路由模块

新建页面文件

const Login = () => {
    return <div>我是登录</div>
}
export default Login

新建router文件

import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login'
import Personal from "../pages/Personal"
const router = createBrowserRouter([
    {
        path: '/Lgoin',
        element: <Login></Login>
    },
    {
        path: '/Personal',
        element: <Personal></Personal>
    }
])
export default router

在index中导入router文件

// 项目的入口,从这里开始运行
// React必要的两个核心包
import React from 'react';
import ReactDOM from 'react-dom/client';

// 导入项目的根组件

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

//导入react-router
import { RouterProvider } from 'react-router-dom';

//导入路由router
import router from './router/router'


// 把App根组件渲染到id为root的dom节点上
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router}></RouterProvider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals

 4.2路由导航跳转

 Login.js文件

import { Link } from "react-router-dom"

const Login = () => {
    return (
        <div>我是登录
            <Link to="/Personal">我是个人</Link></div>
    )
}
export default Login

 

 Login.js文件

import { Link, useNavigate } from "react-router-dom"

const Login = () => {
    const navigate = useNavigate()
    return (
        <div>我是登录
            {/* 声明式写法 */}
            <Link to="/Personal">我是个人</Link>
            {/* 命令式写法 */}
            <button onClick={() => navigate('/Personal')}>跳转个人</button>
        </div>
    )
}
export default Login

4.3路由跳转传参

searchParams传参 


Login.js文件

import { Link, useNavigate } from "react-router-dom"

const Login = () => {
    const navigate = useNavigate()
    return (
        <div>我是登录
            {/* 声明式写法 */}
            <Link to="/Personal">我是个人</Link>
            {/* 命令式写法 */}
            <button onClick={() => navigate('/Personal')}>跳转个人</button>
            <button onClick={() => navigate('/Personal?id=3426&name=zhuman')}>跳转个人传参</button>

        </div>
    )
}
export default Login  

Personal.js文件 

import { useSearchParams } from "react-router-dom"
const Personal = () => {
    const [params] = useSearchParams()
    const id = params.get('id')
    return <div>我是个人{id}</div>
    // return <div>我是个人</div>

}
export default Personal

Params传参


Login.js文件

import { Link, useNavigate } from "react-router-dom"

const Login = () => {
    const navigate = useNavigate()
    return (
        <div>我是登录
            {/* 声明式写法 */}
            <Link to="/Personal">我是个人</Link>
            {/* 命令式写法 */}
            <button onClick={() => navigate('/Personal')}>跳转个人</button>
            <button onClick={() => navigate('/Personal?id=3426&name=zhuman')}>跳转个人传参</button>
            <button onClick={() => navigate('/Personal/3426')}>跳转个人传参</button>

        </div>
    )
}
export default Login  

 src/router/router.js文件

import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login/Login'
import Personal from "../pages/Personal/Personal"
const router = createBrowserRouter([
    {
        path: '/Login',
        element: <Login></Login>
    },
    {
        path: '/Personal/:id',
        element: <Personal></Personal>
    }
])
export default router

 Personal.js文件

import { useParams } from "react-router-dom"
const Personal = () => {
    const params = useParams()
    const id = params.id
    return <div>我是个人 {id}</div>

}
export default Personal

4.4路由嵌套配置

src/router/router.js文件

import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login/Login'
import Personal from "../pages/Personal/Personal"
import About from "../pages/About/About"
import Layout from "../pages/Layout/Layout"
import Board from "../pages/Board/Board"
const router = createBrowserRouter([

    {
        path: '/',
        element: <Layout></Layout>,
        children: [
            {
                path: 'Board',
                element: <Board></Board>
            },
            {
                path: 'About',
                element: <About></About>
            }
        ]
    },
    {
        path: '/Login',
        element: <Login></Login>
    },
    {
        path: '/Personal/:id',
        element: <Personal></Personal>
    }

])
export default router

 src/pages/Layout/Layout.js文件

import { Outlet,Link } from "react-router-dom"

const Layout = () => {
    return (
    <div>我是一级路由
    <Link to="/Board">面板</Link>
    <Link to="/About">关于</Link>
    {/* // 配置二级路由的出口 */}
    <Outlet />
    </div>
    )
}
export default Layout

src/pages/About/About.js文件 -----pages/Board/Board.js文件同理

const About = () =>{
    return <div>我是2级路由About</div>
}
export default About

4.5默认二级路由

src/router/router.js文件

import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login/Login'
import Personal from "../pages/Personal/Personal"
import About from "../pages/About/About"
import Layout from "../pages/Layout/Layout"
import Board from "../pages/Board/Board"
const router = createBrowserRouter([

    {
        path: '/',
        element: <Layout></Layout>,
        children: [
            {
                // 设置为默认二级路由,一级路由访问的时候,它也能得到渲染
                index:true,
                element: <Board></Board>
            },
            {
                path: 'About',
                element: <About></About>
            }
        ]
    },
    {
        path: '/Login',
        element: <Login></Login>
    },
    {
        path: '/Personal/:id',
        element: <Personal></Personal>
    }

])
export default router

 src/pages/Layout/Layout.js文件

import { Outlet,Link } from "react-router-dom"

const Layout = () => {
    return (
    <div>我是一级路由
    <Link to="/">面板</Link>
    <Link to="/About">关于</Link>
    {/* // 配置二级路由的出口 */}
    <Outlet />
    </div>
    )
}
export default Layout

4.6 404路由配置

 src/pages/Layout/Layout.js文件

import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login/Login'
import Personal from "../pages/Personal/Personal"
import About from "../pages/About/About"
import Layout from "../pages/Layout/Layout"
import Board from "../pages/Board/Board"
import NotFound from "../pages/NotFound/NotFound"
const router = createBrowserRouter([

    {
        path: '/',
        element: <Layout></Layout>,
        children: [
            {
                // 设置为默认二级路由,一级路由访问的时候,它也能得到渲染
                index:true,
                element: <Board></Board>
            },
            {
                path: 'About',
                element: <About></About>
            }
        ]
    },
    {
        path: '/Login',
        element: <Login></Login>
    },
    {
        path: '/Personal/:id',
        element: <Personal></Personal>
    },
    // 404页面配置
    {
        path:'*',
        element:<NotFound></NotFound>
    }

])
export default router

 src/pages/NotFound/NotFound.js文件

const NotFound = () => {
    return (
        <div>页面找不到了</div>
    )
}
export default NotFound

4.7两种路由模式

hash模式

import { createBrowserRouter , createHashRouter} from 'react-router-dom'
import Login from '../pages/Login/Login'
import Personal from "../pages/Personal/Personal"
import About from "../pages/About/About"
import Layout from "../pages/Layout/Layout"
import Board from "../pages/Board/Board"
import NotFound from "../pages/NotFound/NotFound"
//Hash模式
const router = createHashRouter([

    {
        path: '/',
        element: <Layout></Layout>,
        children: [
            {
                // 设置为默认二级路由,一级路由访问的时候,它也能得到渲染
                index:true,
                element: <Board></Board>
            },
            {
                path: 'About',
                element: <About></About>
            }
        ]
    },
    {
        path: '/Login',
        element: <Login></Login>
    },
    {
        path: '/Personal/:id',
        element: <Personal></Personal>
    },
    // 404页面配置
    {
        path:'*',
        element:<NotFound></NotFound>
    }

])
export default router

5.记账本项目案例

5.1别名路径配置 

const path = require('path')

module.exports = {
    webpack:{
        alias:{
            '@':path.resolve(__dirname,'src')
        }
    }
}

 

5.2数据Mock实现

5.3整体路由设计

5.4mobile主题定制

全局生效:

 

5.5Redux管理账目列表

// 账单列表相关store
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";

const billStore = createSlice({
    name:'bill',
    // 数据状态state
    initialState: {
        billList: []
    },
    reducers: {
        setBillList(state, action) {
            state.billList = action.payload
        }
    }
})
// 解构actionCraeter函数
const { setBillList } = billStore.actions
// 编写异步
const getBillList = () => {
    return async (dispatch)=>{
        //编写异步请求
        const res = await axios.get('http://localhost:8888/ka')
        // 触发同步reducer
        dispatch(setBillList(res.data))
    }
}
export {getBillList}
// 导出reducer
const reducer = billStore.reducer
export default reducer

 

import { Button } from "antd-mobile"
import { Outlet } from "react-router-dom"
import { useDispatch } from "react-redux"
import { getBillList } from "@/store/billStore/billStore"
import { useEffect } from "react"


const Layout = () => {
    const dispatch = useDispatch()
    useEffect(()=>{
        dispatch(getBillList())
    },[dispatch])
    return (
        <div>
            <Outlet></Outlet>
            Layout
            <Button color="primary">测试全局</Button>
            </div>
    )
}
export default Layout

5.6 TabBar功能实现

UIAnt Design of React - Ant Design

安装scss命令:npm i -D sass

Layout.js文件 

import { TabBar } from "antd-mobile"
import { useEffect } from "react"
import { Outlet, useNavigate } from "react-router-dom"
import { useDispatch } from 'react-redux'
import { getBillList } from "@/store/billStore/billStore"
import './Layout.scss'
import {
  BillOutline,
  CalculatorOutline,
  AddCircleOutline
} from 'antd-mobile-icons'

const tabs = [
  {
    key: '/month',
    title: '月度账单',
    icon: <BillOutline />,
  },
  {
    key: '/new',
    title: '记账',
    icon: <AddCircleOutline />,
  },
  {
    key: '/year',
    title: '年度账单',
    icon: <CalculatorOutline />,
  },
]

const Layout = () => {
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(getBillList())
  }, [dispatch])

  // 切换菜单跳转路由
  const navigate = useNavigate()
  const swithRoute = (path) => {
    console.log(path)
    navigate(path)
  }
  return (
    <div className="layout">
      <div className="container">
        <Outlet />
      </div>
      <div className="footer">
        <TabBar onChange={swithRoute}>
          {tabs.map(item => (
            <TabBar.Item key={item.key} icon={item.icon} title={item.title} />
          ))}
        </TabBar>
      </div>
    </div>
  )
}

export default Layout

 Layout.scss文件

.layout {
  .container {
    position: fixed;
    top: 0;
    bottom: 50px;
    width: 100%;
  }
  .footer {
    position: fixed;
    bottom: 0;
    width: 100%;
  }
}

 

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值