# React 核心语法学习 - 用于快速理解上手

所有的一切都基于您已经非常熟悉 vue 或者其他前端框架,有不错的前端基础

# 一、创建项目

npx create-react-app helloworld
cd helloworld
npm start

vite 创建

本文路由开始的内容是 vite 创建的项目

pnpm create vite helloworld --template react

# 二、基本语法

# 1、插值

import "./App.css";
const divTitle = '标题' 
const divContent =  '内容'
function App() {
  return (
      <div title={divTitle}>
        {divContent}
      </div>
  );
}
export default App;

# 2、条件渲染

import "./App.css";
const divTitle = "标题";
const divContent = "内容";
let flag = true;
const handleFlag = () => {
  return flag ? <h1>{ divTitle}</h1> : <h1>{divContent}</h1>
}
function App() {
  return (
    <>
      <div title={divTitle}>{handleFlag()}</div>
    </>
  );
}
export default App;
import "./App.css";
import { Fragment } from "react";
const list = [
  {
    id: 1,
    name: "React",
  },
  {
    id: 2,
    name: "Angular",
  },
  {
    id: 3,
    name: "Vue",
  },
];
const mapList = () => {
  return list.map((item) => (<Fragment key={item.id}><li >{item.name}</li><li>--------</li></Fragment>));
};
function App() {
  return (
    <>
      <ul>{mapList()}</ul>
    </>
  );
}
export default App;

fragment 我觉得可以当作 vue 的 template 去理解

# 3、事件处理

import "./App.css";
const handleClick = (e) => {
  console.log('我点击了按钮',e);
}
function App() {
  return (
    <>
      <button onClick={handleClick}>按钮</button>
    </>
  );
}
export default App;

# 4、useState 状态处理

import "./App.css";
import { useState } from "react";
function App() {
  const [data, setData] = useState({
    flag: true,
  });
  const handleClick = (e) => {
    setData({
      ...data,
      flag: !data.flag,
    });
  };
  const showContent = () => {
    return data.flag ? "我是内容" : "我是隐藏的内容";
  };
  return (
    <>
      <button onClick={handleClick}>按钮</button>
      <span>{showContent()}</span>
    </>
  );
}
export default App;

# 三、组件传值

# 1、dom 传值

import image from './logo.svg';
function App() {
  const imageData = {
    className: 'small',
    style: {
      width: 200,
      height: '100px',
      backgroundColor: 'gray'
    },
    src: image,
  }
  return (
    <div>
      <img alt="" {...imageData}></img>
    </div>
  );
}
export default App;

# 2、自定义组件传值

import image from "./logo.svg";
import { useState } from "react";
// 文章组件
function Article({ title, content,children }) {
  return (
    <div>
      <h1>{title}</h1>
      {children}
      <p>{content}</p>
    </div>
  );
}
function App() {
  const [articleData, setArticleData] = useState({
    title: "React",
    content: "用于构建用户界面的 JavaScript 库",
  });
  const changeArticleData = () => {
    setArticleData({
      ...articleData,
      title: "vue",
      content: "渐进式JavaScript框架",
    });
  };
  const imageData = {
    className: "small",
    style: {
      width: 200,
      height: "100px",
      backgroundColor: "gray",
    },
    src: image,
  };
  return (
    <div>
      <button onClick={changeArticleData}>按钮</button>
      <img alt="" {...imageData}></img>
      <Article {...articleData} >
        <p>这是一个子组件</p>
      </Article>
    </div>
  );
}
export default App;

image-20240303173417464

# 四、hooks

# 1、useReducer

import { useReducer } from "react";
/**
 * @description: 
 * @param {*} state 当前 reducer 管理的状态
 * @param {*} action  对状态的操作
 * @return {*}
 */
function countReducer(state, action) {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      throw new Error("未知的操作类型");
  }
}
function App() {
  // 计数器
  const [state, dispatch] = useReducer(countReducer, 0) // 0 为初始状态 返回一个数组,第一个元素为当前状态,第二个元素为 dispatch 函数
  // 点击事件
  const handleIncrement = () => {
    dispatch({ type: "increment" });
  };
  const handleDecrement = () => {
    dispatch({ type: "decrement" });
  };
  return (
    <div>
      <h1>计数器</h1>
      <p>{state}</p>
      <button onClick={handleIncrement}>增加</button>
      <button onClick={handleDecrement}>减少</button>
    </div>
  );
}
export default App;
const initialState = {count: 0};
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

肯定会有友友问 useReducer 是什么,我找了一篇文章

一文搞懂 useReducer - 掘金 (juejin.cn)

# 2、useRef

import { useReducer, useRef, useState } from "react";
/**
 * @description: 
 * @param {*} state 当前 reducer 管理的状态
 * @param {*} action  对状态的操作
 * @return {*}
 */
function countReducer(state, action) {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      throw new Error("未知的操作类型");
  }
}
function App() {
  // 计数器
  //const [state, dispatch] = useReducer (countReducer, 0) // 0 为初始状态 返回一个数组,第一个元素为当前状态,第二个元素为 dispatch 函数
  const [count, setCount] = useState(0)
  const preCount = useRef(count)
  // 点击事件
  const handleIncrement = () => {
    // dispatch({ type: "increment" });
    setCount(count + 1)
    preCount.current = count
  };
  const handleDecrement = () => {
    // dispatch({ type: "decrement" });
    setCount(count - 1)
    preCount.current = count;
  };
  return (
    <div>
      <h1>计数器</h1>
      <p>当前:{count}</p>
      <p>上一次:{preCount.current}</p>
      <button onClick={handleIncrement}>增加</button>
      <button onClick={handleDecrement}>减少</button>
    </div>
  );
}
export default App;

React useRef 指南 - 掘金 (juejin.cn)

# 五、React Router

在开始之前 | React Router6 中文文档 (baimingxuan.github.io)

# 1、简单使用

// main.jsx
// 引入一个方法和一个组件
//createBrowserRouter - 创建路由实例 在方法中定义路由 path 和组件的对应关系
// RouterProvider 作为一个组件渲染,并且传入 createBrowserRouter 执行之后生成的 router 实例
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
const router = createBrowserRouter([
  {
    path: '/',
    element: <div>hello world</div>,
  },
])
ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)

# 2、抽离组件

import React from 'react'
import ReactDOM from 'react-dom/client'
import {
  createBrowserRouter,
  RouterProvider
} from 'react-router-dom'
import './index.css'
import Root from '@/router/root'
import ErrorPage from '@/error-page'
const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    errorElement: <ErrorPage />
  },
])
ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)
// error-page.jsx
import { useRouteError } from "react-router-dom";
export default function ErrorPage() {
  const error = useRouteError();
  console.error(error);
  return (
    <div id="error-page">
      <h1>Oops!</h1>
      <p>Sorry, an unexpected error has occurred.</p>
      <p>
        <i>{error.statusText || error.message}</i>
      </p>
    </div>
  );
}

路由的编码可以从 main.jsx 中抽离出来

router/index.jsx

import { createBrowserRouter } from "react-router-dom";
import Root, {
  loader as rootLoader,
  action as rootAction,
} from "@/router/root";
import ErrorPage from "@/error-page";
import Contact from "@/router/contact";
const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    errorElement: <ErrorPage />,
    loader: rootLoader,
    action: rootAction,
    children: [
      {
        path: "/contacts/:contactId",
        element: <Contact />,
      },
    ],
  },
]);
export default router

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import { RouterProvider } from 'react-router-dom'
import router from '@/router'
ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)

# 3、路由模式

和 vue 一样,也是 hash 和 history 两种路由模式。

createBrowserRouter :history 需要后端配合

createHashRouter : hash 不需要后端支持,兼容性更好

# 4、跳转

index.jsx

import { Button } from "antd";
import useRouterHooks from "@/hooks/routerHooks";
export default function IndexAndHome() {
  const { goto } = useRouterHooks();
  return (
    <div>
      <h1>首页</h1>
      <p>我是首页</p>
      <Button type="primary" onClick={() => goto("/login")}>
        login
      </Button>
    </div>
  );
}

login.jsx

import { Button } from 'antd';
import useRouterHooks from '@/hooks/routerHooks';
export default function Login() {
    const { goto } = useRouterHooks();
    return (
      <div>
        <h1>登录</h1>
        <p>我是登录</p>
        <Button onClick={() => goto('/')}>登录</Button>
      </div>
    );
}

router/index.jsx

import { createBrowserRouter } from "react-router-dom";
import ErrorPage from "@/error-page";
import IndexAndHome from "@/views";
import Login from "@/views/login";
const router = createBrowserRouter([
  {
    path: "/",
    element: <IndexAndHome />,
    errorElement: <ErrorPage />,
  },
  {
    path: "/login",
    element: <Login />,
    errorElement: <ErrorPage />,
  },
]);
export default router;

hooks/routerHooks.jsx

import { useNavigate } from "react-router-dom";
export default function useRouterHooks() {
  const navigate = useNavigate();
  /**
   * @description: jump to the specified page
   * @param {*} path
   * @return {*}
   */
  function goto(path) {
    navigate(path);
  }
  return {
    goto,
  };
}

navigate 函数第二个参数是一个对象

其中有一个属性 replace ,为 true 时会导致导航替换历史堆栈中的当前条目,我们升级 routerHooks

import { useNavigate } from "react-router-dom";
export default function useRouterHooks() {
  const navigate = useNavigate();
  /**
   * @description: jump to the specified page
   * @param {*} path
   * @param {*} replace default false | whether to replace the current page
   * @return {*}
   */
  function goto(path, replace = false) {
    navigate(path, { replace });
  }
  return {
    goto,
  };
}

在一些场景中,比如登录之后从历史记录中干掉登录页面或者进登录干掉之前页面的记录也是有需求的。

# 5、路由传参

# 5.1 searchParams

navigate('/about?id=1001')

拿取参数

import { useSearchParams } from 'react-router-dom'
export default function About(){
    const [params] = useSearchParams()
    console.log(params)
    const id = params.get('id')
}

# 5.2 params

navigate('/about/1001')

router 配置文件里也要配置

const router = createBrowserRouter([
    {
        path: '/about/:id', // important
        element: <About />
    }
])

拿取参数

import {useParams} from 'react-router-dom'
export default function About(){
    const params = useParams()
    console.log(params.id)
}

现在我们再次升级我们的 routerHooks,用于支持更加简便的获取参数

hooks/routerHooks

import { useNavigate, useSearchParams, useParams } from "react-router-dom";
export default function useRouterHooks() {
  const navigate = useNavigate();
  const [searchParams, setSearchParams] = useSearchParams();
  const params = useParams();
  /**
   * @description: jump to the specified page
   * @param {*} path
   * @param {*} replace default false | whether to replace the current page
   * @return {*}
   */
  function goto(path, replace = false) {
    navigate(path, { replace });
  }
  function getSearchParams() {
    return { searchParams, setSearchParams };
  }
  function getParams() {
    return params;
  }
  return {
    goto,
    getSearchParams,
    getParams,
  };
}

views/index.jsx

import { Button } from "antd";
import useRouterHooks from "@/hooks/routerHooks";
export default function IndexAndHome() {
  const { goto } = useRouterHooks();
  return (
    <div>
      <h1>首页</h1>
      <p>我是首页</p>
      <Button type="primary" onClick={() => goto("/login?name=anixuil")}>
        login
      </Button>
    </div>
  );
}

views/login.jsx

import { Button } from "antd";
import useRouterHooks from "@/hooks/routerHooks";
export default function Login() {
  const { goto, getSearchParams } = useRouterHooks();
  const { searchParams } = getSearchParams();
  return (
    <div>
      <h1>登录</h1>
      <p>我是登录</p>
      <p>name: {searchParams.get("name")}</p>
      <Button onClick={() => goto("/person/1140040227", true)}>登录</Button>
    </div>
  );
}

views/person.jsx

import useRouterHooks from "@/hooks/routerHooks";
import { Button } from "antd";
export default function Person() {
    const { goto, getParams } = useRouterHooks();
    const { id } = getParams();
    return (
        <div>
        <h1>个人中心</h1>
            <p>我是个人中心</p>
            <p>id: {id}</p>
            <Button type="primary" onClick={() => goto('/')}>go Index</Button>
        </div>
    );
}

router/index.jsx

import { createBrowserRouter } from "react-router-dom";
import ErrorPage from "@/error-page";
import IndexAndHome from "@/views";
import Login from "@/views/login";
import Person from "@/views/person";
const router = createBrowserRouter([
  {
    path: "/",
    element: <IndexAndHome />,
    errorElement: <ErrorPage />,
  },
  {
    path: "/login",
    element: <Login />,
    errorElement: <ErrorPage />,
  },
  {
    path: "/person/:id",
    element: <Person />,
    errorElement: <ErrorPage />,
  },
]);
export default router;

以上便实现了两种方式的路由传参并获取到传参的值展现到页面上

# 6、嵌套路由

  1. 准备二级路由,在路由配置文件中使用 children 对二级路由进行配置
  2. 通过内置组件 Outlet 渲染耳机路由组件
  3. 使用内置组件 Link 进行声明式导航配置

router/index.jsx

import { createBrowserRouter } from "react-router-dom";
import ErrorPage from "@/error-page";
import IndexAndHome from "@/views";
import Login from "@/views/login";
import Person from "@/views/person";
import Layout from "@/views/layout";
const router = createBrowserRouter([
  {
    path: "/",
    element: <IndexAndHome />,
    errorElement: <ErrorPage />,
  },
  {
    path: "/login",
    element: <Login />,
    errorElement: <ErrorPage />,
  },
  {
    path: "/person/:id",
    element: <Person />,
    errorElement: <ErrorPage />,
  },
  {
    path: '/layout',
    element: <Layout />,
    errorElement: <ErrorPage />,
    children: [
      {
        path: 'message',
        element: <div>message</div>,
      },
      {
        path: 'contact',
        element: <div>contact</div>,
      }
    ]
  }
]);
export default router;

views/layout.jsx

import { Outlet, Link } from "react-router-dom";
export default function Layout() {
  return (
    <div>
      <h1>layout</h1>
      <p>我是layout</p>
      <Link to="/layout/message">消息</Link>
      <Link to="/layout/contact">联系人</Link>
      <Outlet />
    </div>
  );
}

# 7、默认 404

import { useRouteError } from "react-router-dom";
export default function ErrorPage() {
  const error = useRouteError();
  console.error(error);
  return (
    <div id="error-page">
      <h1>Oops!</h1>
      <p>Sorry, an unexpected error has occurred.</p>
      <p>
        <i>{error.statusText || error.message}</i>
      </p>
    </div>
  );
}

路由配置里的

errorElement: <ErrorPage />

# 六、Antd

Ant Design - 一套企业级 UI 设计语言和 React 组件库 (antgroup.com)

# 1、安装引入

pnpm i antd --save

# 2、使用

import { Button } from "antd";
...
<Button type="primary">按钮</Button>
...

# 3、自定义主题

main.jsx

import { ConfigProvider } from "antd";
ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <ConfigProvider
      theme=<!--swig0-->
    >
      <RouterProvider router={router} />
    </ConfigProvider>
  </React.StrictMode>
);

# 七、React Redux ToolKit

# 1、安装

pnpm i @reduxjs/toolkit react-redux

# 2、引入使用

store/index.jsx

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

store/modules/counter.jsx

import { createSlice } from '@reduxjs/toolkit';
const counter = createSlice({
    name: 'counter',    // name of the slice  like a namespace
    initialState: {    // initial state of the slice
        count: 0
    },
    // 定义 reducer 更新状态函数
    // 组件中 dispatch 使用的 action.type 就是这里的 key
    reducers: {   // reducers of the slice
        increment: (state,action) => {
            state.count += action.payload
        },
        decrement: (state,action) => {
            state.count -= action.payload
        }
    }
})
// export the action creators and reducers
export const { increment, decrement } = counter.actions;
export default counter.reducer;

main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { RouterProvider } from "react-router-dom";
import router from "@/router";
import { ConfigProvider } from "antd";
import { Provider } from "react-redux";
import store from "@/store";
ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <ConfigProvider
      theme=<!--swig1-->
    >
      <Provider store={store}>
        <RouterProvider router={router} />
        </Provider>
    </ConfigProvider>
  </React.StrictMode>
);

views/index.jsx

import { Button } from "antd";
import useRouterHooks from "@/hooks/routerHooks";
import { useDispatch, useSelector } from "react-redux";
export default function IndexAndHome() {
  const { goto } = useRouterHooks();
  const dispatch = useDispatch();
  const { count } = useSelector((state) => state.counter);
  return (
    <div>
      <h1>首页</h1>
      <p>我是首页</p>
      <Button type="primary" onClick={() => goto("/login?name=anixuil")}>
        login
      </Button>
      <p>counter: {count}</p>
      <Button
        onClick={() => dispatch(increment(1))}
      >
        +
      </Button>
      <Button
        onClick={() => dispatch(decrement(1))}
      >
        -
      </Button>
    </div>
  );
}

image-20240309201406178

# 3、异步

store/modules/counter.jsx

import { createSlice } from "@reduxjs/toolkit";
const counter = createSlice({
  name: "counter", // name of the slice  like a namespace
  initialState: {
    // initial state of the slice
    count: 0,
  },
  // 定义 reducer 更新状态函数
  // 组件中 dispatch 使用的 action.type 就是这里的 key
  reducers: {
    // reducers of the slice
    increment: (state, action) => {
      state.count += action.payload;
    },
    decrement: (state, action) => {
      state.count -= action.payload;
    },
  },
});
// export the action creators and reducers
export const { increment, decrement } = counter.actions;
// 定义异步 action
export const incrementAsync = (amount) => (dispatch) => {
  setTimeout(() => {
    dispatch(increment(amount));
  }, 1000);
};
export const decrementAsync = (amount) => (dispatch) => {
  setTimeout(() => {
    dispatch(decrement(amount));
  }, 1000);
};
export default counter.reducer;

views/index.jsx

import { Button } from "antd";
import useRouterHooks from "@/hooks/routerHooks";
import { useDispatch, useSelector } from "react-redux";
import { increment, decrement, incrementAsync, decrementAsync } from "../store/modules/counter";
export default function IndexAndHome() {
  const { goto } = useRouterHooks();
  const dispatch = useDispatch();
  const { count } = useSelector((state) => state.counter);
  return (
    <div>
      <h1>首页</h1>
      <p>我是首页</p>
      <Button type="primary" onClick={() => goto("/login?name=anixuil")}>
        login
      </Button>
      <p>counter: {count}</p>
      <Button
        onClick={() => dispatch(increment(1))}
      >
        +
      </Button>
      <Button
        onClick={() => dispatch(decrement(1))}
      >
        -
      </Button>
      <Button
        onClick={() => dispatch(incrementAsync(1))}
      >
        async +
      </Button>
      <Button
        onClick={() => dispatch(decrementAsync(1))}
      >
        async -
      </Button>
    </div>
  );
}

# 4、复杂类型实现

数组元素的添加删除

store/modules/counter.jsx

import { createSlice } from "@reduxjs/toolkit";
const counter = createSlice({
  name: "counter", // name of the slice  like a namespace
  initialState: {
    // initial state of the slice
    count: 0,
    list: [],
  },
  // 定义 reducer 更新状态函数
  // 组件中 dispatch 使用的 action.type 就是这里的 key
  reducers: {
    // reducers of the slice
    increment: (state, action) => {
      state.count += action.payload;
    },
    decrement: (state, action) => {
      state.count -= action.payload;
    },
    push: (state, action) => {
      state.list.push(action.payload);
    },
    del: (state, action) => {
      state.list.splice(action.payload, 1);
    },
  },
});
// export the action creators and reducers
export const { increment, decrement, push, del } = counter.actions;
// 定义异步 action
export const incrementAsync = (amount) => (dispatch) => {
  setTimeout(() => {
    dispatch(increment(amount));
  }, 1000);
};
export const decrementAsync = (amount) => (dispatch) => {
  setTimeout(() => {
    dispatch(decrement(amount));
  }, 1000);
};
export default counter.reducer;

views/index.jsx

import { Button } from "antd";
import useRouterHooks from "@/hooks/routerHooks";
import { useDispatch, useSelector } from "react-redux";
import { increment, decrement, incrementAsync, decrementAsync, push, del } from "../store/modules/counter";
export default function IndexAndHome() {
  const { goto } = useRouterHooks();
  const dispatch = useDispatch();
  const { count, list } = useSelector((state) => state.counter);
  return (
    <div>
      <h1>首页</h1>
      <p>我是首页</p>
      <Button type="primary" onClick={() => goto("/login?name=anixuil")}>
        login
      </Button>
      <p>counter: {count}</p>
      <p>list: {list.join(",")}</p>
      <Button onClick={() => dispatch(increment(1))}>+</Button>
      <Button onClick={() => dispatch(decrement(1))}>-</Button>
      <Button onClick={() => dispatch(incrementAsync(1))}>async +</Button>
      <Button onClick={() => dispatch(decrementAsync(1))}>async -</Button>
      <Button onClick={() => dispatch(push(parseInt(Math.random() * 100)))}>
        push
      </Button>
      <Button onClick={() => dispatch(del(list.length - 1))}>del</Button>
    </div>
  );
}
此文章已被阅读次数:正在加载...更新于

请我喝[茶]~( ̄▽ ̄)~*

Anixuil 微信支付

微信支付

Anixuil 支付宝

支付宝