react18+

news2024/9/21 20:35:41

主要是围绕函数式组件讲,18主要用就是函数式组件,学习前先熟悉下原生js的基本使用,主要是事件

1、UI操作

1.1、书写jsx标签语言

基本写法和原生如同一则,只是放在一个方法里面返回而已,我们称这样的写法为函数式组件,组件命名方式为:xxxx.jsx/xxx.tsx,当然也可以不要后面的x

export default function TodoList() {
  return (
    <>
      <h1>海蒂·拉玛的待办事项</h1>
      <img 
        src="https://i.imgur.com/yXOvdOSs.jpg" 
        alt="Hedy Lamarr" 
        className="photo" 
      />
      <ul>
        <li>发明一种新式交通信号灯</li>
        <li>排练一个电影场景</li>
        <li>改进频谱技术</li>
      </ul>
    </>
  );
}

1.2、属性和值的插入

1.2.1、大括号和双大括号插入的使用

使用大括号在渲染的结构上面插入新的结构

export default function TodoList() {
  const name = 'Gregorio Y. Zara';
  return (
    <h1>{name}'s To Do List</h1>
  );
}

使用双大括号插入元素属性

export default function TodoList() {
  return (
    <ul style={{
      backgroundColor: 'black',
      color: 'pink'
    }}>
      <li>Improve the videophone</li>
      <li>Prepare aeronautics lectures</li>
      <li>Work on the alcohol-fuelled engine</li>
    </ul>
  );
}

1.3、props传递

React 组件使用 props 来互相通信。每个父组件都可以提供 props 给它的子组件,从而将一些信息传递给它。Props 可能会让你想起 HTML 属性,但你可以通过它们传递任何 JavaScript 值,包括对象、数组和函数。

其实跟vue的使用差不多:

子组件
function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

父组件:

export default function Profile() {
  return (
    <div>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi', 
          imageId: 'YfeOqp2'
        }}
      />
      <Avatar
        size={80}
        person={{
          name: 'Aklilu Lemma', 
          imageId: 'OKS67lh'
        }}
      />
      <Avatar
        size={50}
        person={{ 
          name: 'Lin Lanying',
          imageId: '1bX5QH6'
        }}
      />
    </div>
  );
}

1.4、条件渲染

这里使用跟vue的v-if完全不一样,是基于原生的js中的if来操作的,结合if判断渲染组件的,这是本质区别。,这里比较灵活,当时同样可以使用三元表达式也是可以的,根据具体开发而定。

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = (
      <del>
        {name + " ✔"}
      </del>
    );
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

1.5、渲染列表

列表渲染会用到循环,当然啦,并不是vue中的那种v-for循环,是用的结合原生的循环方法,比如map,用来循环数据,记住,必须要赋予key,不然后台会提示错误!!

为什么需要key,这里不过多讲,学过vue的就知道不给key会出现意想不到的bug,而且key必须唯一。

key 需要满足的条件 

  • key 值在兄弟节点之间必须是唯一的。 不过不要求全局唯一,在不同的数组中可以使用相同的 key。
  • key 值不能改变,否则就失去了使用 key 的意义!所以千万不要在渲染时动态地生成 key。

渲染的列表:

import { people } from './data.js';

export default function List() {
  const listItems = people.map(person =>
    <li key={person.id}>
      <img
        src=''
        alt={person.name}
      />
      <p>
        <b>{person.name}</b>
          {' ' + person.profession + ' '}
          因{person.accomplishment}而闻名世界
      </p>
    </li>
  );
  return <ul>{listItems}</ul>;
}

渲染的数据:

export const people = [
  {
    id: 0, // 在 JSX 中作为 key 使用
    name: '凯瑟琳·约翰逊',
    profession: '数学家',
    accomplishment: '太空飞行相关数值的核算',
    imageId: 'MK3eW3A',
  },
  {
    id: 1, // 在 JSX 中作为 key 使用
    name: '马里奥·莫利纳',
    profession: '化学家',
    accomplishment: '北极臭氧空洞的发现',
    imageId: 'mynHUSa',
  },
  {
    id: 2, // 在 JSX 中作为 key 使用
    name: '穆罕默德·阿卜杜勒·萨拉姆',
    profession: '物理学家',
    accomplishment: '关于基本粒子间弱相互作用和电磁相互作用的统一理论',
    imageId: 'bE7W1ji',
  },
  {
    id: 3, // 在 JSX 中作为 key 使用
    name: '珀西·莱温·朱利亚',
    profession: '化学家',
    accomplishment: '开创性的可的松药物、类固醇和避孕药',
    imageId: 'IOjWm71',
  },
  {
    id: 4, // 在 JSX 中作为 key 使用
    name: '苏布拉马尼扬·钱德拉塞卡',
    profession: '天体物理学家',
    accomplishment: '白矮星质量计算',
    imageId: 'lrWQx8l',
  },
];

1.6、渲染树

这个本不想多讲,这里过一下吧,不管是原生还是vue都有dom树结构,react也不例外,每一个组件都可以视为一个节点(从App出发-根节点):

 2、交互

2.1、响应事件

其实跟原生的dom操作有点像,不管是dom操作还是事件都可以直接使用的。

给一个简单案例:

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('你点击了 toolbar !');
    }}>
      <button onClick={() => alert('正在播放!')}>
        播放电影
      </button>
      <button onClick={() => alert('正在上传!')}>
        上传图片
      </button>
    </div>
  );
}

2.2、hook的使用

注意:

Hooks ——以 use 开头的函数——只能在组件或自定义 Hook 的最顶层调用。 你不能在条件语句、循环语句或其他嵌套函数内调用 Hook。Hook 是函数,但将它们视为关于组件需求的无条件声明会很有帮助。在组件顶部 “use” React 特性,类似于在文件顶部“导入”模块。

2.2.1、useState的使用

剖析 useState 

当你调用 useState 时,你是在告诉 React 你想让这个组件记住一些东西:

const [index, setIndex] = useState(0);

在这个例子里,你希望 React 记住 index

useState 的唯一参数是 state 变量的初始值。在这个例子中,index 的初始值被useState(0)设置为 0

每次你的组件渲染时,useState 都会给你一个包含两个值的数组:

  1. state 变量 (index) 会保存上次渲染的值。
  2. state setter 函数 (setIndex) 可以更新 state 变量并触发 React 重新渲染组件。

以下是实际发生的情况:

const [index, setIndex] = useState(0);

  1. 组件进行第一次渲染。 因为你将 0 作为 index 的初始值传递给 useState,它将返回 [0, setIndex]。 React 记住 0 是最新的 state 值。
  2. 你更新了 state。当用户点击按钮时,它会调用 setIndex(index + 1)。 index 是 0,所以它是 setIndex(1)。这告诉 React 现在记住 index 是 1 并触发下一次渲染。
  3. 组件进行第二次渲染。React 仍然看到 useState(0),但是因为 React 记住 了你将 index 设置为了 1,它将返回 [1, setIndex]
  4. 以此类推!

案例1:

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

  function handleNextClick() {
    setIndex(index + 1);
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i> 
        by {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Hide' : 'Show'} details
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
    </>
  );
}

案例2:

import { useState } from 'react';
export default function MovingDot() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });
  return (
    <div
      onPointerMove={e => {
        setPosition({
          x: e.clientX,
          y: e.clientY
        });
      }}
      style={{
        position: 'relative',
        width: '100vw',
        height: '100vh',
      }}>
      <div style={{
        position: 'absolute',
        backgroundColor: 'red',
        borderRadius: '50%',
        transform: `translate(${position.x}px, ${position.y}px)`,
        left: -10,
        top: -10,
        width: 20,
        height: 20,
      }} />
    </div>
  );
}

3、状态管理

3.1、useReducer的使用

对比 useState 和 useReducer 

Reducers 并非没有缺点!以下是比较它们的几种方法:

  • 代码体积: 通常,在使用 useState 时,一开始只需要编写少量代码。而 useReducer 必须提前编写 reducer 函数和需要调度的 actions。但是,当多个事件处理程序以相似的方式修改 state 时,useReducer 可以减少代码量。
  • 可读性: 当状态更新逻辑足够简单时,useState 的可读性还行。但是,一旦逻辑变得复杂起来,它们会使组件变得臃肿且难以阅读。在这种情况下,useReducer 允许你将状态更新逻辑与事件处理程序分离开来。
  • 可调试性: 当使用 useState 出现问题时, 你很难发现具体原因以及为什么。 而使用 useReducer 时, 你可以在 reducer 函数中通过打印日志的方式来观察每个状态的更新,以及为什么要更新(来自哪个 action)。 如果所有 action 都没问题,你就知道问题出在了 reducer 本身的逻辑中。 然而,与使用 useState 相比,你必须单步执行更多的代码。
  • 可测试性: reducer 是一个不依赖于组件的纯函数。这就意味着你可以单独对它进行测试。一般来说,我们最好是在真实环境中测试组件,但对于复杂的状态更新逻辑,针对特定的初始状态和 action,断言 reducer 返回的特定状态会很有帮助。
  • 个人偏好: 并不是所有人都喜欢用 reducer,没关系,这是个人偏好问题。你可以随时在 useState 和 useReducer 之间切换,它们能做的事情是一样的!

如果你在修改某些组件状态时经常出现问题或者想给组件添加更多逻辑时,我们建议你还是使用 reducer。当然,你也不必整个项目都用 reducer,这是可以自由搭配的。你甚至可以在一个组件中同时使用 useStateuseReducer

import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
import tasksReducer from './tasksReducer.js';

export default function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

  function handleAddTask(text) {
    dispatch({
      type: 'added',
      id: nextId++,
      text: text,
    });
  }

  function handleChangeTask(task) {
    dispatch({
      type: 'changed',
      task: task,
    });
  }

  function handleDeleteTask(taskId) {
    dispatch({
      type: 'deleted',
      id: taskId,
    });
  }

  return (
    <>
      <h1>布拉格的行程安排</h1>
      <AddTask onAddTask={handleAddTask} />
      <TaskList
        tasks={tasks}
        onChangeTask={handleChangeTask}
        onDeleteTask={handleDeleteTask}
      />
    </>
  );
}

let nextId = 3;
const initialTasks = [
  {id: 0, text: '参观卡夫卡博物馆', done: true},
  {id: 1, text: '看木偶戏', done: false},
  {id: 2, text: '打卡列侬墙', done: false},
];


export default function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('未知 action:' + action.type);
    }
  }
}

3.2、Context的使用

app.jsx

import Heading from './Heading.js';
import Section from './Section.js';

export default function Page() {
  return (
    <Section>
      <Heading>主标题</Heading>
      <Section>
        <Heading>副标题</Heading>
        <Heading>副标题</Heading>
        <Heading>副标题</Heading>
        <Section>
          <Heading>子标题</Heading>
          <Heading>子标题</Heading>
          <Heading>子标题</Heading>
          <Section>
            <Heading>子子标题</Heading>
            <Heading>子子标题</Heading>
            <Heading>子子标题</Heading>
          </Section>
        </Section>
      </Section>
    </Section>
  );
}

sections.js

import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Section({ children }) {
  const level = useContext(LevelContext);
  return (
    <section className="section">
      <LevelContext.Provider value={level + 1}>
        {children}
      </LevelContext.Provider>
    </section>
  );
}

heading.js

import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Heading({ children }) {
  const level = useContext(LevelContext);
  switch (level) {
    case 0:
      throw Error('Heading 必须在 Section 内部!');
    case 1:
      return <h1>{children}</h1>;
    case 2:
      return <h2>{children}</h2>;
    case 3:
      return <h3>{children}</h3>;
    case 4:
      return <h4>{children}</h4>;
    case 5:
      return <h5>{children}</h5>;
    case 6:
      return <h6>{children}</h6>;
    default:
      throw Error('未知的 level:' + level);
  }
}

LevelContext.js

import { createContext } from 'react';

export const LevelContext = createContext(0);

写在你使用 context 之前 

使用 Context 看起来非常诱人!然而,这也意味着它也太容易被过度使用了。如果你只想把一些 props 传递到多个层级中,这并不意味着你需要把这些信息放到 context 里。

在使用 context 之前,你可以考虑以下几种替代方案:

  1. 从 传递 props 开始。 如果你的组件看起来不起眼,那么通过十几个组件向下传递一堆 props 并不罕见。这有点像是在埋头苦干,但是这样做可以让哪些组件用了哪些数据变得十分清晰!维护你代码的人会很高兴你用 props 让数据流变得更加清晰。
  2. 抽象组件并 将 JSX 作为 children 传递 给它们。 如果你通过很多层不使用该数据的中间组件(并且只会向下传递)来传递数据,这通常意味着你在此过程中忘记了抽象组件。举个例子,你可能想传递一些像 posts 的数据 props 到不会直接使用这个参数的组件,类似 <Layout posts={posts} />。取而代之的是,让 Layout 把 children 当做一个参数,然后渲染 <Layout><Posts posts={posts} /></Layout>。这样就减少了定义数据的组件和使用数据的组件之间的层级。

如果这两种方法都不适合你,再考虑使用 context。

Context 的使用场景 

  • 主题: 如果你的应用允许用户更改其外观(例如暗夜模式),你可以在应用顶层放一个 context provider,并在需要调整其外观的组件中使用该 context。
  • 当前账户: 许多组件可能需要知道当前登录的用户信息。将它放到 context 中可以方便地在树中的任何位置读取它。某些应用还允许你同时操作多个账户(例如,以不同用户的身份发表评论)。在这些情况下,将 UI 的一部分包裹到具有不同账户数据的 provider 中会很方便。
  • 路由: 大多数路由解决方案在其内部使用 context 来保存当前路由。这就是每个链接“知道”它是否处于活动状态的方式。如果你创建自己的路由库,你可能也会这么做。
  • 状态管理: 随着你的应用的增长,最终在靠近应用顶部的位置可能会有很多 state。许多遥远的下层组件可能想要修改它们。通常 将 reducer 与 context 搭配使用来管理复杂的状态并将其传递给深层的组件来避免过多的麻烦。

Context 不局限于静态值。如果你在下一次渲染时传递不同的值,React 将会更新读取它的所有下层组件!这就是 context 经常和 state 结合使用的原因。

一般而言,如果树中不同部分的远距离组件需要某些信息,context 将会对你大有帮助。

3.3、使用 Reducer 和 Context 拓展你的应用

Reducer 可以整合组件的状态更新逻辑。Context 可以将信息深入传递给其他组件。你可以组合使用它们来共同管理一个复杂页面的状态。

这点不做详细介绍,参考官网:

使用 Reducer 和 Context 拓展你的应用 – React 中文文档

4、拖围机制

有些组件可能需要控制和同步 React 之外的系统。例如,你可能需要使用浏览器 API 聚焦输入框,或者在没有 React 的情况下实现视频播放器,或者连接并监听远程服务器的消息。在本章中,你将学习到一些脱围机制,让你可以“走出” React 并连接到外部系统。大多数应用逻辑和数据流不应该依赖这些功能。

4.1、ref的使用

当你希望组件“记住”某些信息,但又不想让这些信息 触发新的渲染 时,你可以使用 ref

const ref = useRef(0);

与 state 一样,ref 在重新渲染之间由 React 保留。但是,设置 state 会重新渲染组件,而更改 ref 不会!你可以通过 ref.current 属性访问该 ref 的当前值。

import { useRef } from 'react';

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('你点击了 ' + ref.current + ' 次!');
  }

  return (
    <button onClick={handleClick}>
      点我!
    </button>
  );
}

ref 就像组件的一个不被 React 追踪的秘密口袋。例如,可以使用 ref 来存储 timeout ID、DOM 元素 和其他不影响组件渲染输出的对象。

4.1.1、使用 ref 操作 DOM 

由于 React 会自动更新 DOM 以匹配渲染输出,因此组件通常不需要操作 DOM。但是,有时可能需要访问由 React 管理的 DOM 元素——例如聚焦节点、滚动到此节点,以及测量它的尺寸和位置。React 没有内置的方法来执行此类操作,所以需要一个指向 DOM 节点的 ref 来实现。例如,点击按钮将使用 ref 聚焦输入框:

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        聚焦输入框
      </button>
    </>
  );
}

4.2、 Effect的使用

有些组件需要与外部系统同步。例如,可能需要根据 React 状态控制非 React 组件、设置服务器连接或在组件出现在屏幕上时发送分析日志。与处理特定事件的事件处理程序不同,Effect 在渲染后运行一些代码。使用它将组件与 React 之外的系统同步。

多按几次播放/暂停,观察视频播放器如何与 isPlaying 属性值保持同步:

import { useState, useRef, useEffect } from 'react';

function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  useEffect(() => {
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
  }, [isPlaying]);

  return <video ref={ref} src={src} loop playsInline />;
}

export default function App() {
  const [isPlaying, setIsPlaying] = useState(false);
  return (
    <>
      <button onClick={() => setIsPlaying(!isPlaying)}>
        {isPlaying ? '暂停' : '播放'}
      </button>
      <VideoPlayer
        isPlaying={isPlaying}
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      />
    </>
  );
}

许多 Effect 也会自行“清理”。例如,与聊天服务器建立连接的 Effect 应该返回一个 cleanup 函数,告诉 React 如何断开组件与该服务器的连接:

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

export default function ChatRoom() {
  useEffect(() => {
    const connection = createConnection();
    connection.connect();
    return () => connection.disconnect();
  }, []);
  return <h1>欢迎前来聊天!</h1>;
}


export function createConnection() {
  // 真正的实现实际上会连接到服务器
  return {
    connect() {
      console.log('✅ 连接中...');
    },
    disconnect() {
      console.log('❌ 断开连接。');
    }
  };
}

在开发环境中,React 将立即运行并额外清理一次 Effect。这就是为什么你会看到 "✅ 连接中..." 打印了两次。这能够确保你不会忘记实现清理功能。

4.3、响应式 Effect 的生命周期

Effect 的生命周期不同于组件。组件可以挂载、更新或卸载。Effect 只能做两件事:开始同步某些东西,然后停止同步它。如果 Effect 依赖于随时间变化的 props 和 state,这个循环可能会发生多次。

这个 Effect 依赖于 roomId props 的值。props 是 响应值,这意味着它们可以在重新渲染时改变。注意,如果 roomId 更改,Effect 将会 重新同步(并重新连接到服务器):

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  return <h1>欢迎来到 {roomId} 房间!</h1>;
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  return (
    <>
      <label>
        选择聊天室:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">所有</option>
          <option value="travel">旅游</option>
          <option value="music">音乐</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}



export function createConnection(serverUrl, roomId) {
  // 真正的实现实际上会连接到服务器
  return {
    connect() {
      console.log('✅ 连接到 "' + roomId + '" 房间,在' + serverUrl + '...');
    },
    disconnect() {
      console.log('❌ 断开 "' + roomId + '" 房间,在' + serverUrl);
    }
  };
}

4.4、使用自定义 Hook 复用逻辑 

React 有一些内置 Hook,例如 useStateuseContextuseEffect。有时需要用途更特殊的 Hook:例如获取数据,记录用户是否在线或者连接聊天室。为了实现效果,可以根据应用需求创建自己的 Hook。

这个示例中,自定义 Hook usePointerPosition 追踪当前指针位置,而自定义 Hook useDelayedValue 返回一个“滞后”传递的值一定毫秒数的值。将光标移到沙盒预览区域上以查看跟随光标移动的点轨迹:

import { usePointerPosition } from './usePointerPosition.js';
import { useDelayedValue } from './useDelayedValue.js';

export default function Canvas() {
  const pos1 = usePointerPosition();
  const pos2 = useDelayedValue(pos1, 100);
  const pos3 = useDelayedValue(pos2, 200);
  const pos4 = useDelayedValue(pos3, 100);
  const pos5 = useDelayedValue(pos4, 50);
  return (
    <>
      <Dot position={pos1} opacity={1} />
      <Dot position={pos2} opacity={0.8} />
      <Dot position={pos3} opacity={0.6} />
      <Dot position={pos4} opacity={0.4} />
      <Dot position={pos5} opacity={0.2} />
    </>
  );
}

function Dot({ position, opacity }) {
  return (
    <div style={{
      position: 'absolute',
      backgroundColor: 'pink',
      borderRadius: '50%',
      opacity,
      transform: `translate(${position.x}px, ${position.y}px)`,
      pointerEvents: 'none',
      left: -20,
      top: -20,
      width: 40,
      height: 40,
    }} />
  );
}





import { useState, useEffect } from 'react';

export function usePointerPosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  useEffect(() => {
    function handleMove(e) {
      setPosition({ x: e.clientX, y: e.clientY });
    }
    window.addEventListener('pointermove', handleMove);
    return () => window.removeEventListener('pointermove', handleMove);
  }, []);
  return position;
}



import { useState, useEffect } from 'react';

export function useDelayedValue(value, delay) {
  const [delayedValue, setDelayedValue] = useState(value);

  useEffect(() => {
    setTimeout(() => {
      setDelayedValue(value);
    }, delay);
  }, [value, delay]);

  return delayedValue;
}

你可以创建自定义 Hooks,将它们组合在一起,在它们之间传递数据,并在组件之间重用它们。随着应用不断变大,你将减少手动编写的 Effect,因为你将能够重用已经编写的自定义 Hooks。React 社区也维护了许多优秀的自定义 Hooks。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1948140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

牛客JS题(三)文件扩展名

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; 正则表达式可选链操作符 题干&#xff1a; 我的答案 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /></head><body><script>/*** 可能…

Vue前端页面嵌入mermaid图表--流程图

一、安装Mermaid 首先&#xff0c;你需要在你的项目中安装Mermaid。可以通过npm或yarn来安装&#xff1a; npm install mermaid --save # 或者 yarn add mermaid结果如图&#xff1a; 二、Vue 方法一&#xff1a;使用pre标签 使用ref属性可以帮助你在Vue组件中访问DOM元素 …

JAVA同城服务场馆门店预约系统支持H5小程序APP源码程序

&#x1f525;同城服务场馆门店预约系统&#xff1a;一键预约&#xff0c;便捷生活新体验&#xff01;&#x1f389; &#x1f31f;告别排队等待&#xff0c;预约新风尚&#x1f31f; 你还在为去游泳馆、健身房或是瑜伽馆排队等待而烦恼吗&#xff1f;同城服务场馆门店预约系统…

渲染技术如何帮助设计内容实现从平面到立体的转换

随着数字艺术和视觉特效的飞速发展&#xff0c;三维建模与渲染技术在影视、游戏、广告、工业设计、建筑可视化等多个领域展现出了其不可或缺的重要性。这一技术不仅实现了从平面到立体的跨越&#xff0c;还极大地丰富了视觉表达的层次感和真实感。 三维建模&#xff1a;构建虚…

模拟电子技术-实验四 二极管电路仿真

实验四 二极管电路仿真 一&#xff0e;实验类型 验证性实验 二&#xff0e;实验目的 1、验证二极管的单向导电性 2、验证二极管的稳压特性。 三&#xff0e;实验原理 二极管的单向导电性&#xff1a; 四、实验内容 1、二极管参数测试仿真实验 1&#xff09;仪表仿真…

【Linux】线程互斥和同步

目录 线程互斥 相关概念 互斥量mutex 互斥量的接口 初始化互斥量 销毁互斥量 互斥量加锁/解锁 可重入VS线程安全 概念 可重入与线程安全的联系 可重入与线程安全的区别 死锁 死锁的四个必要条件 避免死锁 避免死锁的算法 线程同步 条件变量 条件变量函数 初始…

【已解决】ModuleNotFoundError: No module named ‘numpy’

【已解决】ModuleNotFoundError: No module named ‘numpy’ 在Python编程中&#xff0c;遇到“ModuleNotFoundError: No module named ‘numpy’”这样的错误提示并不罕见。这个错误意味着Python解释器无法在你的环境中找到名为numpy的模块。numpy是Python中一个非常重要的库…

ElasticSearch(四)— 数据检索与查询

一、基本查询语法 所有的 REST 搜索请求使用_search 接口&#xff0c;既可以是 GET 请求&#xff0c;也可以是 POST请求&#xff0c;也可以通过在搜索 URL 中指定索引来限制范围。 _search 接口有两种请求方法&#xff0c;一种是基于 URI 的请求方式&#xff0c;另一种是基于…

JavaScript第一天

变量的基本使用 更新变量 let age 18age 19 用户名输入案例&#xff1a; let uname prompt(请输入姓名) document.write(uname) 这样在提示框中输入姓名之后&#xff0c;就会在网页中显示出来 当输入之后不在网页中显示的时候&#xff0c;可能是变量名写错了&#xf…

C++笔记---缺省参数和函数重载

1. 缺省参数 1.1 定义 缺省参数是声明或定义函数时为函数的参数指定一个缺省值&#xff08;默认值&#xff09;。在调用该函数时&#xff0c;如果没有指定实参 则采用该形参的缺省值&#xff0c;否则使用指定的实参&#xff0c;缺省参数分为全缺省和半缺省参数。 void Func(…

Linux源码阅读笔记14-IO体系结构与访问设备

IO体系结构 与外设通信通常称为输入输出&#xff0c;一般缩写为I/O。在实现外设IO的时候&#xff0c;内核必须处理三个可能出现的问题&#xff1a; 必须根据具体的设备类型和模型&#xff0c;使用各种方法对硬件寻址。内核必须向用户应用程序和系统工具提供访问各种设备的方法…

【Git多人协作开发】同一分支下的多人协作开发模式

目录 0.前言场景 1.开发者1☞完成准备工作&协作开发 1.1创建dev分支开发 1.2拉取远程dev分支至本地 1.3查看分支情况和分支联系情况 1.4创建本地dev分支且与远程dev分支建立联系 1.5在本地dev分支上开发file.txt 1.6推送push至远程仓库 2.开发者2☞完成准备工作&…

性能测试工具、负载测试工具、缺陷跟踪工具推荐

负载测试工具 - 有助于对站点或应用程序进行性能/负载测试 1&#xff09;WebLOAD WebLOAD 是一款出色的测试工具&#xff0c;提供了许多强大的脚本功能&#xff0c;有助于测试复杂场景。该工具支持从 Selenium 到移动端、从企业应用到网络协议的数百种技术。使用这款工具可以…

Java | Leetcode Java题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; class Solution {public int numSquares(int n) {if (isPerfectSquare(n)) {return 1;}if (checkAnswer4(n)) {return 4;}for (int i 1; i * i < n; i) {int j n - i * i;if (isPerfectSquare(j)) {return 2;}}return 3;}// 判断是否为…

nVisual综合布线预标签解决方案

1 跳线变更前编制标签编码、打印标签、粘贴标签费时费力&#xff1b; 2 无标签、假标签、标签错误问题造成后期无法查询线缆连接关系&#xff1b; 1 跳线出厂时在跳线两端预粘贴条码标签&#xff0c;为每条跳线设置唯一ID&#xff1b; 2 跳线变更完成后&…

MySQL练手 --- 1251. 平均售价

题目链接&#xff1a;1251. 平均售价 思路&#xff1a; 由题意可知&#xff0c;Prices表和UnitsSold表&#xff0c;表的连接关系为一对一&#xff0c;连接字段&#xff08;匹配字段&#xff09;为product_id 要求&#xff1a;查找每种产品的平均售价。而Prices表含有价格还有…

Vue与ASP.NET Core Web Api设置localhost与本地ip地址皆可访问

Vue的设置 我们创建并启动一个Vue项目&#xff0c;如下所示&#xff1a; 打开cmd&#xff0c;输入ipconfig查询本地ip地址&#xff1a; 想通过本地ip地址访问&#xff0c;把localhost改成本地ip地址&#xff0c;发现打不开&#xff1a; 这是因为Vue项目默认只有localhost&…

二级医院LIS系统源码,医学检验系统,支持DB2,Oracle,MS SQLServer等主流数据库

系统概述&#xff1a; LIS系统即实验室信息管理系统。LIS系统能实现临床检验信息化&#xff0c;检验科信息管理自动化。其主要功能是将检验科的实验仪器传出的检验数据经数据分析后&#xff0c;自动生成打印报告&#xff0c;通过网络存储在数据库中&#xff0c;使医生能够通过医…

Stage模型应用程序包结构

目录 官网地址 官网结构图 开发态包结构 工程目录结构 配置文件 module.json5配置文件 app.json5配置文件 官网地址 官网地址 包结构 官网结构图 开发态包结构 在DevEco Studio上创建一个项目工程&#xff0c;并尝试创建多个不同类型的Module&#xff08;类似一个一个的页…

基于区块链技术的高校教育资源共享的研究

&#xff08;一&#xff09;项目背景 时代变迁下的高教管理革新需求 当前&#xff0c;我国高等教育体系深受行政化管理模式影响&#xff0c;其在指引办学方向、资源优化配置及院校稳定上功不可没。然而&#xff0c;随着社会主义市场经济体系的深化发展&#xff0c;该模式逐渐显…