记 17 React项目结构及基础语法

90 阅读4分钟

新建React项目

npx create-react-app my-react-app (项目名) (不再是npm 而是npx) 创建成功后cd 项目名进入项目目录 npm start 启动项目

React项目结构

public内是静态页面资源 src内是源码,其中包括初学者最关注的index.js和App.js

index.js(项目入口文件)

文件内引入关键词库 React 和 ReactDOM 通过ReactDOM的createRoot方法创建ReactDOMRoot实例 最后通过Render进行根组件的渲染

App.js(项目根组件)

组件中创建了一个普通函数App,并将其导出(函数组件)

React相关语法及结构

React组件的两种创建方式

  • 函数组件(主推)
  • 类组件(较复杂/冗rong余)

React语法 JS + HTML = JSX

  • 在React中,html结构被放在函数的返回值中,return 后使用圆括号()突破js原语法,表明内部是html结构
  • JSX只能返回单个根元素,如果不想加额外容器(div),可以加JSX提供的空标签(<> </>), 相当于vue的template
  • React中的每个元素都必须正确的闭合
  • React中的属性通常都是驼峰式命名
  return (
    <>
      <img src={logo} className="App-logo" alt="logo" />
    </>
  );

插值

类似于vue的插值,双括号变为单括号{ },可以同时应用于标签内容和标签属性

条件渲染

function App() {
  const flag = false
  let divContent
  if (flag) {
    divContent = <span>Good Morning</span>
  } else {
    divContent = <p>Good Afternoon</p>
  }
  return (
    <>
      <div>{divContent}</div>
    </>
  );
}

列表渲染

function App() {
  const list = [
    // 一般不以index作为key,因为index可能会发生变化
    { id: 18, name: '张三' },
    { id: 19, name: '李四' }
  ]
  // 相比于forEach,map有返回值,可以返回一个新数组
  const listContent = list.map(item => (
    //JSX中只能有一个根元素,所以需要用一个空标签包裹
    //如果循环中每次都存在多个根元素,那么就需要使用Fragment
    <Fragment key={item.id}>
      <li>{item.name}</li>
      <p>_________________</p>
    </Fragment>
  ))

  return (
    <ul>{listContent}</ul>
  );
}

事件

function App() {
  function handleClick(){
    console.log("Button Clicked");
  }
  return (
    <button onClick={handleClick}></button>
  );
}

Hooks : useState状态处理

函数式组件默认没有状态,其中的数据只是普通变量,不能实时响应。 为此React提供了一个函数useState,需要传入数据的初始值为参数,函数调用后会返回一个数组[content,setContent],第一个值是对当前数据的引用(本次渲染的数据内容),第二个参数是用来修改状态的函数(一个读一个写)。通过函数来调用并传入新内容。

useState处理变量

function App() {
  //解构useState返回的数组
  const [content, setDivContent] = useState('Hello World')
  function handleClick() {
    setDivContent('Hello React')
  }
  return (
    <>
      <div>{content}</div>
      <button onClick={handleClick}></button>
    </>
  );
}

useState处理对象

function App() {
  const [data, setData] = useState({
    content: 'Hello React',
    title: 'Hello World',
  })
  function handleClick() {
    //setData会把data里的所有属性都替换掉
    //所以要保留原来的属性,需要使用扩展运算符
    setData({
      ...data,
      title: 'Hello',
    })
  }
  return (
    <>
      <div title={data.title}>{data.content}</div>
      <button onClick={handleClick}></button>
    </>
  );
}

useState处理数组

function App() {
  const [data, setData] = useState([
    { id: 18, name: '张三' },
    { id: 19, name: '李四' }
  ])
  
  const listContent = data.map(item => (
    <li key={item.id}>{item.name}</li>
  ))

  function handleClick() {
    setData(data.filter(item => item.id!== 2))
  }

  return (
    <>
      <ul>{listContent}</ul>
      <button onClick={handleClick}></button>
    </>
  );
}

React Hooks 实现同步useState

React的useState在set某个值之后虽然视图回刷新,但是无法直接在js中获取到最新的值(更新是异步的),无法实现类似于赋值后回调的操作。但是useRef的特性正好跟useState相反,是可以直接在js中同步更新但是视图不会刷新

简单来说就是组合useState和useRef,制造一个新的hooks来替代useState,在jsx这种照样像useState一样使用,但是在需要做同步代码(回调)的时候使用ref去读取,保证能获取到最新的值。

import { MutableRefObject, useRef, useState } from "react";

/**
 * 定义一个同步状态管理的钩子,用于在React组件中同步管理状态
 * 它提供了初始状态设置、状态更新和状态引用的功能
 */
export type UseSyncState = <T = any>(
  initialValue: T,
) => [T, (input: T) => void, MutableRefObject<T>];

/**
 * 实现useSyncState钩子
 * @param initialValue 初始状态值
 * @returns 返回一个包含当前状态、更新状态函数和状态引用的数组
 */
export const useSyncState: UseSyncState = <T = any,>(initialValue: T) => {
  // 使用useState钩子管理组件的状态
  const [state, setState] = useState(initialValue);

  // 使用useRef钩子创建一个状态的ref,用于在组件更新时保持对当前状态的引用
  const stateRef = useRef<T>(state);
    
  /**
   * 同步更新状态的函数,支持传入新的状态值或一个函数,该函数接收上一个状态并返回新的状态
   * @param v
   */
  const syncSetState: (input: T) => void = (v) => {
    setState(v);
    stateRef.current = v;
  };

  // 返回当前状态、更新状态的函数和状态的ref
  return [state, syncSetState, stateRef];
};

使用

import { useSyncState } from "@/utils/hooks/useSyncState";
import { Button, Space } from "antd";

export const Test: React.FC = () => {
  const [count, setCount, countRef] = useSyncState(0);

  const handleAddCount = () => {
    setCount(countRef.current + 1); //直接能获取到最新的值进行计算
    console.log(countRef.current); // 直接能打印出最新的值
  };

  return (
    <div>
      <Space>
        {/* 视图会显示出最新的值 */}
        {count}
        <Button onClick={handleAddCount}></Button>
      </Space>
    </div>
  );
};

export default Test;

因为永远可以获取到最新的值,所以setState函数不再需要通过函数的方式传入。