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

前言

为什么要开始学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 的函数。

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

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

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

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

```javascript

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

const theme = useContext(ThemeContext); ```

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

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

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

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

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

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

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

context的使用

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

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

```javascript

const MyContext = React.createContext(defaultValue);

```

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

```javascript

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

```

  1. 消费上下文值:有两种方式可以在子组件中消费上下文的值。一种方式是用 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 手动传递。

文件传输部分

文件传输

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

javascript 复制代码
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);
    // 文件上传成功,调用api
    console.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}>
    <Dragger
      name="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来进行上传

javascript 复制代码
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的接口,就像这样,核心函数如下:

javascript 复制代码
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标签里就成

html 复制代码
 <video ref={videoRef} autoPlay muted className={style.show}/>

结语

总之先别急,慢慢来就成

相关推荐
呜呼~225146 分钟前
前后端数据交互
java·vue.js·spring boot·前端框架·intellij-idea·交互·css3
神雕杨7 分钟前
node js 过滤空白行
开发语言·前端·javascript
网络安全-杰克17 分钟前
《网络对抗》—— Web基础
前端·网络
m0_7482507417 分钟前
2020数字中国创新大赛-虎符网络安全赛道丨Web Writeup
前端·安全·web安全
周伯通*19 分钟前
策略模式以及优化
java·前端·策略模式
艾斯特_32 分钟前
前端代码装饰器的介绍及应用
前端·javascript
Sokachlh36 分钟前
【elementplus】中文模式
前端·javascript
轻口味36 分钟前
【每日学点鸿蒙知识】hap安装报错、APP转移账号、import本地文件、远程包构建问题、访问前端页面方法
前端·华为·harmonyos
m0_7482453443 分钟前
BY组态-低代码web可视化组件
前端·低代码
web182854825121 小时前
ctfshow-web 151-170-文件上传
前端·状态模式