1. React Hooks #

1.1 Hooks 优点 #

1.2 自定义 Hook #

e4fa49ed9ad67984a49e90cbe1d0b111

1.3 初始化项目 #

cra zhufeng_custom_hooks
cd zhufeng_custom_hooks
npm install express cors morgan bootstrap react-router-dom --save

2.useRequest #

2.1 index.js #

src\index.js

import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css'
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Table from './Table';
const App = () => (
  <div className="container">
    <div className="row">
      <div className="col-md-12" style={{ padding: 10 }}>
        <Router>
          <ul className="nav nav-tabs">
            <li><Link to="/table">Table</Link></li>
          </ul>
          <Routes>
            <Route path="/table" element={<Table />}/>
          </Routes>
        </Router>
      </div>
    </div>
  </div>
);
const root = document.getElementById('root');
const reactRoot = ReactDOM.createRoot(root);
reactRoot.render(<App />);

2.2 Table.js #

src\Table.js

import React from 'react';
import useRequest from './hooks/useRequest';
const URL = 'http://localhost:8000/api/users';

export default function Table() {
    const [data, options, setOptions] = useRequest(URL);
    const { currentPage, totalPage, list } = data;
    return (
        <>
            <table className="table table-striped">
                <thead>
                    <tr><td>ID</td><td>姓名</td></tr>
                </thead>
                <tbody>
                    {
                        list.map(item => (<tr key={item.id}><td>{item.id}</td><td>{item.name}</td></tr>))
                    }
                </tbody>
            </table>
            <nav>
                <ul className="pagination">
                    {currentPage > 1 && (
                        <li className="page-item">
                            <button className="page-link" onClick={() => setOptions({ ...options, currentPage: currentPage - 1 })}>
                                <span>&laquo;</span>
                            </button>
                        </li>
                    )}
                    {
                        new Array(totalPage).fill(0).map((_, index) => (
                            <li className={index + 1 === currentPage ? 'page-item active' : 'page-item'} key={index}>
                                <button className="page-link" onClick={() => setOptions({ ...options, currentPage: index + 1 })}>{index + 1}</button>
                            </li>
                        ))
                    }
                    {
                        currentPage < totalPage && (
                            <li className="page-item">
                                <button className="page-link" onClick={() => setOptions({ ...options, currentPage: currentPage + 1 })}>
                                    <span>&raquo;</span>
                                </button>
                            </li>
                        )
                    }
                </ul>
            </nav>
        </>
    )
}

2.3 useRequest.js #

src\hooks\useRequest.js

import { useState, useEffect } from 'react';
function useRequest(url) {
    let [options, setOptions] = useState({
        currentPage: 1,
        pageSize: 5
    });
    let [data, setData] = useState({
        totalPage: 0,
        list: []
    });
    function getData() {
        let { currentPage, pageSize } = options;
        fetch(`${url}?currentPage=${currentPage}&pageSize=${pageSize}`)
            .then(response => response.json())
            .then(result => {
                setData({...result});
            });
    }
    useEffect(getData, [options, url]);
    return [data,options, setOptions];
}

export default useRequest;

2.4 api.js #

api.js

let express = require('express');
let cors = require('cors');
let logger = require('morgan');
let app = express();
app.use(logger('dev'));
app.use(cors());
app.get('/api/users', function (req, res) {
    let currentPage = parseInt(req.query.currentPage);
    let pageSize = parseInt(req.query.pageSize);
    let total=25;
    let list = [];
    let offset = (currentPage-1)*pageSize;
    for (let i = offset; i < offset + pageSize; i++) {
        list.push({ id: i + 1, name: 'name' + (i + 1) });
    }
    res.json({
        currentPage,
        pageSize,
        totalPage:Math.ceil(total/pageSize),
        list
    });
});
app.listen(8000,()=>{
    console.log('sever started at port 8000');
});

3.useDrag #

c81b23738e0b951ad11d4a395795b7ff

3.1 基础 #

3.1.1 触摸事件 #

事件名称 描述 是否包含 touches 数组
touchstart 触摸开始发
touchmove 滑动时接触点改变
touchend 手指离开屏幕时触摸结束

3.1.2 触摸列表 #

参数 描述
touches 当前位于屏幕上的所有手指的列表
targetTouches 位于当前DOM元素上手指的列表

3.1.3 Touch对象 #

参数 描述
clientX 触摸目标在视口中的x坐标
clientY 触摸目标在视口中的y坐标
pageX 触摸目标在页面中的x坐标
pageY 触摸目标在页面中的y坐标

3.2 实现 #

3.2.1 index.js #

src\index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import 'bootstrap/dist/css/bootstrap.css'
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Table from './Table';
+import Drag from './Drag';
const App = () => (
  <div className="container">
    <div className="row">
      <div className="col-md-12" style={{ padding: 10 }}>
        <Router>
          <ul className="nav nav-tabs">
            <li><Link to="/table">Table</Link></li>
          </ul>
          <Routes>
            <Route path="/table" element={<Table />}/>
+            <Route path="/drag" element={<Drag />}/>
          </Routes>
        </Router>
      </div>
    </div>
  </div>
);
const root = document.getElementById('root');
const reactRoot = ReactDOM.createRoot(root);
reactRoot.render(<App />);

3.2.2 src\Drag.js #

src\Drag.js

import React from 'react';
import useDrag from './hooks/useDrag';
let style = { width: '100px', height: '100px', borderRadius: '50%' };
export default function Drag() {
    const [style1, dragRef1] = useDrag();
    const [style2, dragRef2] = useDrag();
    return (
        <>
            <div
                ref={dragRef1}
                style={{ ...style, backgroundColor: 'red', transform: `translate(${style1.x}px, ${style1.y}px)` }}
            ></div>
            <div
                ref={dragRef2}
                style={{ ...style, backgroundColor: 'green', transform: `translate(${style2.x}px, ${style2.y}px)` }}
            ></div>
        </>
    );
}

3.2.3 useDrag.js #

src\hooks\useDrag.js

import { useEffect, useState, useRef } from 'react';
// 引入React的必要的库
function useDrag() {
    // 定义一个自定义Hook useDrag
    const [position, setPosition] = useState({ x: 0, y: 0 });
    // 使用useState设置一个position状态,初始化为{ x: 0, y: 0 }
    const moveElement = useRef(null);
    // 使用useRef创建一个可变的对象moveElement,它的.current属性初始化为null
    useEffect(() => {
        // 使用useEffect来执行副作用函数
        const start = (event) => {
            // 定义一个start函数,它会在触摸开始时被调用
            const { clientX, clientY } = event.targetTouches[0];
            // 从event的targetTouches中获取触摸点的clientX和clientY
            const startX = clientX;
            const startY = clientY;
            // 将获取到的clientX和clientY分别赋值给startX和startY
            const move = (event) => {
                // 定义一个move函数,它会在触摸移动时被调用
                const { clientX, clientY } = event.targetTouches[0];
                // 同样的,从event的targetTouches中获取触摸点的clientX和clientY
                const newPosX = position.x + (clientX - startX);
                const newPosY = position.y + (clientY - startY);
                // 计算新的位置,新的x坐标为当前position.x加上触摸点clientX与startX的差值,
                // 新的y坐标为当前position.y加上触摸点clientY与startY的差值
                setPosition({ x: newPosX, y: newPosY });
                // 使用setPosition更新position的状态
            };
            const end = () => {
                // 定义一个end函数,它会在触摸结束时被调用
                moveElement.current.removeEventListener('touchmove', move);
                moveElement.current.removeEventListener('touchend', end);
                // 在触摸结束时移除move和end事件监听器
            };
            moveElement.current.addEventListener('touchmove', move);
            moveElement.current.addEventListener('touchend', end);
            // 在触摸开始时添加move和end事件监听器
        };
        moveElement.current.addEventListener('touchstart', start);
        // 添加start事件监听器
        return () => {
            moveElement.current.removeEventListener('touchstart', start);
            // 在组件卸载或重新渲染时移除start事件监听器
        };
    }, [position]);
    // useEffect的依赖数组包含position,这意味着当position变化时,useEffect会重新运行

    return [position, moveElement];
    // 返回position状态和moveElement引用
}
export default useDrag;
// 导出useDrag Hook

4.useForm #

4.1 src\index.js #

src\index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import 'bootstrap/dist/css/bootstrap.css'
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Table from './Table';
import Drag from './Drag';
+import Form from './Form';
const App = () => (
  <div className="container">
    <div className="row">
      <div className="col-md-12" style={{ padding: 10 }}>
        <Router>
          <ul className="nav nav-tabs">
            <li><Link to="/table">table</Link></li>
            <li><Link to="/drag">drag</Link></li>
            <li><Link to="/form">form</Link></li>
          </ul>
          <Routes>
            <Route path="/table" element={<Table />}/>
            <Route path="/drag" element={<Drag />}/>
+           <Route path="/form" element={<Form />}/>
          </Routes>
        </Router>
      </div>
    </div>
  </div>
);
const root = document.getElementById('root');
const reactRoot = ReactDOM.createRoot(root);
reactRoot.render(<App />);

4.2 src\Form.js #

src\Form.js

import React, { useCallback } from 'react';
import useForm from './hooks/useForm';
export default function Form() {
    const [formData, setFormValue, resetFormValues] = useForm({ username: '', email: '' });
    const onUsernameChange = useCallback((event) => {
        setFormValue('username', event.target.value);
    }, [setFormValue]);
    const onEmailChange = useCallback((event) => {
        setFormValue('email', event.target.value);
    }, [setFormValue]);
    return (
        <div className="panel">
            <div className="panel-body">
                <form>
                    <div className="form-group">
                        <label>用户名</label>
                        <input
                            className="form-control"
                            placeholder="用户名"
                            value={formData.username}
                            onChange={onUsernameChange}
                        />
                    </div>
                    <div className="form-group">
                        <label>邮箱</label>
                        <input
                            className="form-control"
                            placeholder="邮箱"
                            value={formData.email}
                            onChange={onEmailChange}
                        />
                    </div>
                    <button type="button" className="btn btn-default" onClick={() => console.log(formData)}>提交</button>
                    <button type="button" className="btn btn-default" onClick={resetFormValues}>重置</button>
                </form>
            </div>
        </div>
    );
}

4.3 useForm.js #

src\hooks\useForm.js

import { useState } from 'react';
function useForm(values) {
    const [formData, setFormData] = useState(values);
    const setFormValue = (key, value) => {
        setFormData(prevFormData => ({ ...prevFormData, [key]: value }));
    };
    const resetFormValues = () => {
        setFormData(values);
    };
    return [formData, setFormValue, resetFormValues];
}
export default useForm;

5.useAnimation #

5.1 src\index.js #

src\index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import 'bootstrap/dist/css/bootstrap.css'
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Table from './Table';
import Drag from './Drag';
import Form from './Form';
+import Animation from './Animation';
const App = () => (
  <div className="container">
    <div className="row">
      <div className="col-md-12" style={{ padding: 10 }}>
        <Router>
          <ul className="nav nav-tabs">
            <li><Link to="/table">table</Link></li>
            <li><Link to="/drag">drag</Link></li>
            <li><Link to="/form">form</Link></li>
+           <li><Link to="/animation">animation</Link></li>
          </ul>
          <Routes>
            <Route path="/table" element={<Table />}/>
            <Route path="/drag" element={<Drag />}/>
            <Route path="/form" element={<Form />}/>
+           <Route path="/animation" element={<Animation />}/>
          </Routes>
        </Router>
      </div>
    </div>
  </div>
);
const root = document.getElementById('root');
const reactRoot = ReactDOM.createRoot(root);
reactRoot.render(<App />);

5.2 src\Animation.js #

src\Animation.js

import useAnimation from './hooks/useAnimation';
import './Animation.css';
function Animation() {
  const [className, start] = useAnimation('animation','active');
    return (
      <div className={className} onClick={start}></div>
    );
}
export default Animation;;

5.3 CiAnimationrcle.css #

src\Animation.css

.animation {
    width : 200px;
    height : 200px;
    background-color : gray;
    transition: all 2s;
}
.animation.active {
    background-color : green;
}

5.4 useAnimation.js #

src\hooks\useAnimation.js

import {useState} from 'react';
function useAnimation(initialClassName,activeClassName) {
    const [className, setClassName] = useState(initialClassName);
    function start() {
        if (className === initialClassName) {
            setClassName(`${initialClassName} ${activeClassName}`);
        }else{
            setClassName(`${initialClassName}`);
        }
    }
    return [className, start];
}
export default useAnimation;