npx create-react-app npm i express morgan cors bootstrap react-router-dom
1、useRequest
1-1、index.js
import React from 'react';import ReactDOM from 'react-dom';import 'bootstrap/dist/css/bootstrap.css'import { BrowserRouter, Link, Route } from 'react-router-dom';import Table from './pages/Table'import Drag from './pages/Drag'import Animation from './pages/Animation'import Form from './pages/Form'ReactDOM.render(<div className="container"><div className='row'><div className='col-md-12'><BrowserRouter><Link to='table'>Table</Link> |<Link to='drag'>Drag</Link> |<Link to='form'>Form</Link> |<Link to='animation'>Animation</Link><Route path='/table' component={Table}/><Route path='/drag' component={Drag}/><Route path='/animation' component={Animation}/><Route path='/form' component={Form}/></BrowserRouter></div></div></div>,document.getElementById('root'))
1-2、page/table.js
import React from "react";import useRequest from '../hooks/useRequest'const URL = 'http://localhost:8000/api/users'/** useRequest 自定义Hook 用来请求远程接口* @params url String* @return Array [data,options,setOptions]*/export default function(){const [data,options,setOptions] = useRequest(URL);// 当前页 总页数 本页数组const { currentPage, totalPage,list } = data;console.log(data,'data');return (<><table><thead><tr><td>ID</td><td>NAME</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'>{new Array(totalPage).fill(0).map((item,index)=>(<li key={index}><buttononClick={()=>setOptions({...options,currentPage:index+1})}className='btn btn-primary'>{index+1}</button></li>))}</ul></nav></>)}
1-3、hooks/useRequest.js
import { useState, useEffect } from "react";/** useRequest 自定义Hook 用来请求远程接口* @params url String* @return Array [data,options,setOptions]*/function useRequest(url,initOptions) {let [options, setOptions] = useState(Object.assign({currentPage: 1,pageSize: 5,},initOptions));let [data, setData] = useState({totalPage: 0,list: [],});// 调用接口,返回数据function getData() {let { currentPage, pageSize } = options;fetch(`${url}?currentPage=${currentPage}&pageSize=${pageSize}`).then((res) => res.json()).then((res) => {setData({ ...res });});}//* 依赖项 options 或者 url发生变化 会重新发送请求/useEffect(getData, [options, url]);return [data, options, setOptions];}export default useRequest;
1-4、servies.js
const express = require('express')const cors = require('cors')const logger = require('morgan')const app = express()app.use(cors())app.use(logger('dev'))// http://localhost:8000/api/users?currentPage=1&pageSize=5app.get('/api/users',function(req,res){let currentPage = parseInt(req.query.currentPage) // 1 , 2, 3let pageSize = parseInt(req.query.pageSize) // 5let total = 25;let list = []let offset = (currentPage-1) * pageSizefor(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,function(){console.log('app 启动起来了')})
2、useDrag
2-1、hooks/useDrag.js
import { useState,useEffect, useLayoutEffect, useRef } from 'react';function useDrag(){// DOM位置 useRef 保存对象 可以在组件多次渲染的时候 保持不变const positionRef = useRef({currentX:0,currentY:0,lastX:0,lastY:0})const domRef = useRef(null) // domRef.current = div真是的DOM元素const [, forceUpdate ] = useState({})useLayoutEffect(() => { // 执行时机更早 想尽快的绑定事件 useEffect 什么时候绑定就不一定了let startX,startY; // 拖拽开始的x、y坐标const start = function(event){const { clientX, clientY } = event.targetTouches[0];startX = clientX;startY = clientY;domRef.current.addEventListener('touchmove',move)domRef.current.addEventListener('touchend',end)}const move = function(event){const { clientX , clientY } = event.targetTouches[0];positionRef.current.currentX = positionRef.current.lastX + (clientX - startX)positionRef.current.currentY = positionRef.current.lastY + (clientY - startY)forceUpdate({})}const end = function(){positionRef.current.lastX = positionRef.current.currentXpositionRef.current.lastY = positionRef.current.currentYdomRef.current.removeEventListener('touchmove',move)domRef.current.removeEventListener('touchend',end)}domRef.current.addEventListener('touchstart',start)}, [])// 让那个dom元素进行移动let style = {x: positionRef.current.currentX, y:positionRef.current.currentY}return [style,domRef]}export default useDrag;
2-2、 pages/Drag.js
import React from "react";import useDrag from "../hooks/useDrag";const basicStyle = { width: "100px", height: "100px", borderRadius: "50%" };// 自定义本质 是逻辑的复用,而非数据的复用export default function Drag() {const [style1, dragRef1] = useDrag();const [style2, dragRef2] = useDrag();return (<><divref={dragRef1}style={{...basicStyle,background: "red",transform: `translate(${style1.x}px,${style1.y}px)`}}></div><divref={dragRef2}style={{...basicStyle,background: "yellow",transform: `translate(${style2.x}px,${style2.y}px)`,}}></div></>);}
3、useForm
3-1、hooks/useForm.js
import { useState } from 'react';//useRef只能放在函数里function useForm(initialValues) {const [formData, setFormData] = useState(initialValues);const setFormValue = (key, value) => {setFormData({ ...formData, [key]: value });}const resetFormValues = () => {setFormData(initialValues);}return [formData, setFormValue, resetFormValues];}export default useForm;
3-2、page/Form.js
import React,{useCallback} from "react";import useForm from '../hooks/useForm'function Form(){const [ formData, setFormValue, resetFormValues ] = useForm({username:'',email:''})const changeUsername = useCallback((e)=>setFormValue('username',e.target.value),[setFormValue])const changeEmail = useCallback((e)=>setFormValue('email',e.target.value),[setFormValue])return (<form><div className="form-group"><label>用户名</label><input className="form-control" placeholder="用户名"value={formData.username}onChange={changeUsername}/></div><div className="form-group"><label>邮箱</label><input className="form-control" placeholder="邮箱"value={formData.email}onChange={changeEmail}/></div><button type="button" className="btn btn-primary" onClick={()=>console.log(formData)}>提交</button><button type="button" className="btn btn-primary" onClick={()=>resetFormValues()}>重置</button></form>)}export default Form;
4、useAnimation
4-1、hooks/useAnimation.js
import { useState } from "react";function useAnimate(baseClassName, activeClassName) {let [className, setClassName] = useState(baseClassName);const toggle = () => {if (className === baseClassName) {setClassName(`${baseClassName} ${activeClassName}`);} else {setClassName(`${baseClassName}`);}};return [className, toggle];}export default useAnimate;
4-2、page/Animate.js
import React from "react";import useAnimation from "../hooks/useAnimation";import "./Animation.css";export default function Animation() {const [className, toggle] = useAnimation("circle", "active");return <div className={className} onClick={toggle}></div>;}
4-3、page/Animate.css
.circle {width: 200px;height: 200px;transition: all 3s;border-radius: 50%;background: #ff0;}.circle.active {background: #f00;}
5、useDebounce
防抖setState
import { useEffect, useState } from "react";function useDebounce(value: any, delay = 300) {const [debouncedValue, setDebouncedValue] = useState(value);useEffect(() => {const handler = window.setTimeout(() => {setDebouncedValue(value);}, delay);return () => {clearTimeout(handler);};}, [value, delay]);return debouncedValue;}export default useDebounce;
6、useClickOutside
点击目标元素外面区域触发事件
import {RefObject,useEffect} from 'react'function useClickOutside(ref: RefObject<HTMLElement>,handler:Function) {useEffect(() => {const listener = (event:MouseEvent) => {if(!ref.current || ref.current.contains(event.target as HTMLElement)){return}handler(event)}window.addEventListener('click',listener)return () => {window.removeEventListener('click',listener)}}, [ref, handler])}export default useClickOutside
