个人网站开(九)五系统前端react

devtools/2024/9/25 3:38:57/

前言

为什么要开始学react呢,感觉很大原因是因为本人最近拿到了团子的暑期实习offer,想着先熟悉熟悉技术栈,所以开始学习react了

正文

总之先开篇讲一下我的react学习方法,先大概看了B站的视频,然后就是去看网上的教程,最后就是直接开写,毕竟实践出真知,这个系统主要做的是文件传输,包括普通文件和音频文件和视频文件,还有他们的管理,所以写起来还是遇到了很多的问题的......

react适应

因为我原先没用过react,所以上手还是花了点时间的,不过在我大概写了几个页面之后我就差不多明白了,而且我现在一般只用函数式去写react组件,这样的话调用hooks实际上和vue3的相差无几,所以用起来也是比较熟练了,那么在这个基础上我觉得值得说的就是下面这几个点:

各种钩子函数(hooks)

React 的 Hooks 有很多,这里列出并说明一些主要的钩子函数:

1. **useState**: 它是最常用的 Hook,允许你在函数组件中添加 state。例如:

```javascript

const [count, setCount] = useState(0);

```

这个 Hook 接受初始 state 作为参数,返回一对值:当前 state 以及更新 state 的函数。

2. **useEffect**: 这个 Hook 可以让你执行副作用操作(数据获取,订阅,手动更改 DOM 等等)。它接受两个参数,一个是包含副作用逻辑的函数,一个是依赖数组。

```javascript useEffect(() => { // 这里是副作用逻辑

document.title = `You clicked ${count} times`; }, [count]); // 当 count 变化时执行副作用 ```

3. **useContext**: 这个 Hook 接受一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。就像这样:

```javascript

const ThemeContext = React.createContext('light');

const theme = useContext(ThemeContext); ```

4. **useReducer**: 这个 Hook 是 useState 的替代方案,接受类型 (state, action) => newState 的 reducer,并返回与 dispatcher 方法配对的当前 state。

```javascript

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); //... } ```

5. **useCallback**: 返回一个记忆化版本的回调函数。 ```javascript const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], ); ```

6. **useMemo**: 返回一个记忆化的值。 ```javascript const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); ```

7. **useRef**: 返回一个可改变的 ref 对象,其 .current 属性被初始化为传递的参数(initialValue)。返回的对象在组件的全生命周期内保持不变。 ```javascript const refContainer = useRef(initialValue); ```

8. **useImperativeHandle**: 用于让你在使用 ref 时自定义暴露给父组件的实例值。这样,父组件的“refs”就会指向某个特定的值,而不是子组件实例。 ```javascript useImperativeHandle(ref, createHandle, [deps]) ```

9. **useLayoutEffect**: 和 useEffect 相似,不过它发生在更早的时间──React 更新 DOM,浏览器更新视窗后,然后再执行这个 Hook。 ```javascript useLayoutEffect(() => { // 在浏览器绘制新的 UI 之后,但在屏幕的下一次绘制之前,synchronously }); ```

10. **useDebugValue**: 可以在 React 开发者工具中显示自定义 hook 的标签。 ```javascript useDebugValue(value) ``` 

context的使用

在 React 中,Context 提供了一种方式可以让数据在组件树中传递而不必一层一层手动传递 props。这在很多场景下都很有用,例如:

在应用中的很多组件需要用到同样一份数据,或者需要在深层嵌套的组件中传递数据。 使用 React Context 的基本步骤可以分为以下三步: 1. 创建上下文:使用 React.createContext() 方法可以创建一个 Context 对象。

```javascript

const MyContext = React.createContext(defaultValue);

```

2. 提供上下文值:在你的组件树中,用 Context.Provider 组件包裹起你需要将数据传递给其下所有的子组件的部分。

```javascript

<MyContext.Provider value={/* some value */}>

```

3. 消费上下文值:有两种方式可以在子组件中消费上下文的值。一种方式是用 Context.Consumer 组件。

```javascript

<MyContext.Consumer> {value => /* render something based on the context value */} </MyContext.Consumer>

```

另一种方式是用 useContext Hook。

```javascript

const value = useContext(MyContext);

```

以下是一个使用 Context 的完整例子:

```javascript

// 创建一个上下文并设置默认值为 'light'

const ThemeContext = React.createContext('light');

// 父组件

function App() { return (

// 使用 ThemeContext.Provider 为子组件提供值

<ThemeContext.Provider value="dark">

<Toolbar /> </ThemeContext.Provider> ); }

// 中间组件,即使这一层不用使用这个 context,也需要将它传递下去

function Toolbar() { return ( <div> <Button /> </div> ); }

// 子组件,这里使用 context function Button() { /

/ 使用 useContext Hook 消费 context

const theme = useContext(ThemeContext); return <button>{theme}</button>; }

```

在这个例子中,我们首先创建了一个 ThemeContext,并设置了默认值为 'light'。然后我们在 App 组件中用 ThemeContext.Provider 提供了一个 context 值为 'dark'。最后在 Button 组件中我们用 useContext Hook 消费了这个 context,将 Button 的内容设置为当前主题。如此我们就实现了跨组件传递数据,而无需通过 props 手动传递。

文件传输部分

文件传输

这个部分其实就是改写了一下组件的上传函数,具体代码如下:

import React, { useState } from "react";
import { InboxOutlined } from "@ant-design/icons";
import { message, Upload } from "antd";
import api from "../../api/document"; // 引入你写的api
import styles from './index.module.css'
const { Dragger } = Upload;const App = () => {const [fileList, setFileList] = useState([]);const onChange = async ({ file, fileList }) => {setFileList(fileList);if (file.status === "uploading") {} else if (file.status === "done") {}};const handleUpload = async ({ file, onSuccess, onError }) => {const uid = localStorage.getItem("uid");const response = await api.sendDocument(uid, file);// 文件上传成功,调用apiconsole.log("上传", file);if (response.error) {console.error("上传文件时发生错误:", response.error);message.error(`上传文件时发生错误:${response.error}`);onError(new Error(response.error));} else {message.success(`${file.name} 文件上传成功.`);onSuccess(null, response);}};const onDrop = (e) => {console.log("Dropped files", e.dataTransfer.files);};const onError = (error) => {console.error("上传文件时发生错误:", error);message.error(`上传文件时发生错误:${error}`);};return (<div className={styles.detile}><Draggername="file"multiple={true}fileList={fileList}onChange={onChange}onDrop={onDrop}onError={onError}action="#" // 从"/api/document"文件中找到这个URL,并填到这里来customRequest={handleUpload} ><p className="ant-upload-drag-icon"><InboxOutlined /></p><p className="ant-upload-text">点击或将文件拖拽到这个区域来上传</p><p className="ant-upload-hint">支持单个或批量上传. 严禁上传公司数据及其他禁止的文件.</p></Dragger></div>);
};export default App;

唯一值得一提的大概是具体的上传部分用了formdata来进行上传

const sendDocument = async (uid, file) => {let formData = new FormData();formData.append("uid", uid);formData.append("file", file);try {const response = await axios({method: "post",url: `${process.env.REACT_APP_API_URL}/document`,data: formData,headers: {"Content-Type": "multipart/form-data;charset=utf-8", //加上字符编码信息},});return response.data;} catch (error) {console.error("Error during API Call", error);return { error: error.message };}
};

`FormData` 是 Web API 的一个接口,它提供了一种表示表单数据(键值对)的方法,可以使用 XMLHttpRequest.send() 方法发送,同时它还可以让你通过 append() 方法来添加新的键值对到现有的 FormData 对象中。以下是关于 FormData 如何使用的一些基本指南。

**创建 FormData 对象** 创建一个新的 FormData 对象时,你可以选择是否传入一个 HTML `<form>` 元素作为参数。如果传入了,那么这个 FormData 对象会使用这个表单的当前键/值对进行填充。

```javascript

// 创建一个空的 FormData 对象

const formData = new FormData();

// 用表单元素填充 FormData 对象

const formElement = document.querySelector("form");

const formDataWithForm = new FormData(formElement); ```

**添加和读取数据** 可以用 `append()` 方法向 FormData 对象添加新的键值对,用 `get()` 方法来读取键值对。

```javascript

const formData = new FormData();

formData.append("username", "jinxu chen");

console.log(formData.get("username"));

// 输出 "jinxu chen" ```

**向服务器发送 FormData**

要向服务器发送 FormData,最常用的方法是通过 fetch API 或者 XMLHttpRequest。

```javascript

// 使用 fetch API

fetch("/api/endpoint", { method: "POST", body: formData });

// 使用 XMLHttpRequest

const xhr = new XMLHttpRequest();

xhr.open("POST", "/api/endpoint");

xhr.send(formData); ```

音频录制和上传

上传部分倒是老一套的formdata,值得一讲的大概是录制部分?这里其实也是调用web的接口,就像这样,核心函数如下:

const Sound = () => {const [recording, setRecording] = useState(false);const [audioURL, setAudioURL] = useState(null);const mediaRecorderRef = useRef(null);const audioChunksRef = useRef([]);const now = new Date();const year = now.getFullYear();const month = String(now.getMonth() + 1).padStart(2, '0');const day = String(now.getDate()).padStart(2, '0');const hours = String(now.getHours()).padStart(2, '0');const minutes = String(now.getMinutes()).padStart(2, '0');const seconds = String(now.getSeconds()).padStart(2, '0');const fileName = `${year}${month}${day}_${hours}${minutes}${seconds}.wav`;const startRecording = () => {navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {mediaRecorderRef.current = new MediaRecorder(stream);audioChunksRef.current = [];mediaRecorderRef.current.addEventListener("dataavailable", event => {console.log("dataavailable event has been triggered");audioChunksRef.current.push(event.data);});mediaRecorderRef.current.start(10);setRecording(true);}).catch(err => {message.error('获取音频流失败');console.log(err);});};const stopRecording = () => {mediaRecorderRef.current.stop();setRecording(false);const audioBlob = new Blob(audioChunksRef.current);console.log("audio data", audioChunksRef.current, audioBlob)const audioURL = URL.createObjectURL(audioBlob);console.log("audio url", audioURL)setAudioURL(audioURL);};

视频录制和上传

这边的话其实和音频也是如出一辙,唯一可以提一下的大概是如何将视频的数据流实时展示:

其实很简单,就是把数据流放到video标签里就成

 <video ref={videoRef} autoPlay muted className={style.show}/>

结语

总之先别急,慢慢来就成


http://www.ppmy.cn/devtools/4347.html

相关文章

电感与磁珠

电感是什么&#xff1f; 电感会通过产生感应电动势的方式来阻碍电流的变化&#xff0c;电流变化率越大&#xff0c;产生的感应电动势越大阻碍电流效果越明显。 [一]品质因数Q: 电感的品质因数Q值定义&#xff1a;电感的Q值也叫作品质因数&#xff0c;其为无功功率除以有功功率…

Redis系列2:数据持久化提高可用性

1 介绍 从上一篇的 《深刻理解高性能Redis的本质》 中可以知道&#xff0c; 我们经常在数据库层上加一层缓存&#xff08;如Redis&#xff09;&#xff0c;来保证数据的访问效率。 这样性能确实也有了大幅度的提升&#xff0c;但是本身Redis也是一层服务&#xff0c;也存在宕机…

公园高速公路景区校园IP网络广播音柱SIP音柱

公园高速公路景区校园IP网络广播音柱SIP音柱 适用于学校、车站、教堂、工厂、仓库、公园停车场及露天市场高速公路等场所播放录制语音文件或背景音乐节目&#xff0c;专业一体化音箱设计&#xff0c;高强度防水设计&#xff0c;符合IP54防护等认证&#xff0c;数字化产品&…

HCIP的学习(8)

OSPF数据报文 OSPF头部信息&#xff08;公共固定&#xff09; 版本&#xff1a;OSPF版本&#xff0c;在IPv4网络中版本字段恒定为数值2&#xff08;v1属于实验室版本&#xff0c;v3属于IPv6&#xff09;类型&#xff1a;代表具体是哪一种报文&#xff0c;按照1~5排序&#xff…

constinit

类的静态成员在类内初始化 class Test{ public: const static int var1; } 这个变量的值不可修改&#xff1b;

Conmi的正确答案——ESP32获取MAC地址

ESP-IDF版本&#xff1a;v5.2.1 ESP32芯片型号&#xff1a;ESP32C3&#xff08;4M flash版本&#xff09; ESP支持的MAC地址有&#xff1a; typedef enum {ESP_MAC_WIFI_STA, /**< MAC for WiFi Station (6 bytes) */ESP_MAC_WIFI_SOFTAP, /**< MAC for WiFi Sof…

go拼接字符串的方法

相信大家在工作中遇到不少拼接字符串的情况&#xff0c;而且就是应为字符串的拼接导致程序在一定的情况下变慢&#xff0c;今天我就把多重拼接字符串的方式放在下面&#xff0c;大家可以根据自己的需要选择不同的拼接方法 func TestJoinString(t *testing.T) {dataInfo : []st…

ChatGPT在应用在STM32 HAL库 I2C从机硬件中断代码 回答的错误

ChatGPT也会有错&#xff0c;给出的方案是通用性&#xff0c;偏人为理解的&#xff0c;但是由于上一条原因&#xff0c;提供的代码供要用技术原理甄别&#xff0c;出错不容易查&#xff0c;能够给予工程师启发&#xff0c;直接使用前必须有自己的思考&#xff0c;主观认为它能用…