use
开头,函数内部可以调用其他的 Hook
cra zhufeng_custom_hooks
cd zhufeng_custom_hooks
npm install express cors morgan bootstrap react-router-dom --save
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 />);
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>«</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>»</span>
</button>
</li>
)
}
</ul>
</nav>
</>
)
}
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;
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');
});
事件名称 | 描述 | 是否包含 touches 数组 |
---|---|---|
touchstart | 触摸开始发 | 是 |
touchmove | 滑动时接触点改变 | 是 |
touchend | 手指离开屏幕时触摸结束 | 是 |
参数 | 描述 |
---|---|
touches | 当前位于屏幕上的所有手指的列表 |
targetTouches | 位于当前DOM元素上手指的列表 |
参数 | 描述 |
---|---|
clientX | 触摸目标在视口中的x坐标 |
clientY | 触摸目标在视口中的y坐标 |
pageX | 触摸目标在页面中的x坐标 |
pageY | 触摸目标在页面中的y坐标 |
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 />);
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>
</>
);
}
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
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 />);
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>
);
}
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;
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 />);
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;;
src\Animation.css
.animation {
width : 200px;
height : 200px;
background-color : gray;
transition: all 2s;
}
.animation.active {
background-color : green;
}
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;