npx create-react-app npm i express morgan cors bootstrap react-router-dom

1、useRequest

1-1、index.js

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import 'bootstrap/dist/css/bootstrap.css'
  4. import { BrowserRouter, Link, Route } from 'react-router-dom';
  5. import Table from './pages/Table'
  6. import Drag from './pages/Drag'
  7. import Animation from './pages/Animation'
  8. import Form from './pages/Form'
  9. ReactDOM.render(
  10. <div className="container">
  11. <div className='row'>
  12. <div className='col-md-12'>
  13. <BrowserRouter>
  14. <Link to='table'>Table</Link> |
  15. <Link to='drag'>Drag</Link> |
  16. <Link to='form'>Form</Link> |
  17. <Link to='animation'>Animation</Link>
  18. <Route path='/table' component={Table}/>
  19. <Route path='/drag' component={Drag}/>
  20. <Route path='/animation' component={Animation}/>
  21. <Route path='/form' component={Form}/>
  22. </BrowserRouter>
  23. </div>
  24. </div>
  25. </div>,
  26. document.getElementById('root')
  27. )

1-2、page/table.js

  1. import React from "react";
  2. import useRequest from '../hooks/useRequest'
  3. const URL = 'http://localhost:8000/api/users'
  4. /*
  5. * useRequest 自定义Hook 用来请求远程接口
  6. * @params url String
  7. * @return Array [data,options,setOptions]
  8. */
  9. export default function(){
  10. const [data,options,setOptions] = useRequest(URL);
  11. // 当前页 总页数 本页数组
  12. const { currentPage, totalPage,list } = data;
  13. console.log(data,'data');
  14. return (
  15. <>
  16. <table>
  17. <thead><tr><td>ID</td><td>NAME</td></tr></thead>
  18. <tbody>
  19. {
  20. list.map(item=>(
  21. <tr key={item.id}>
  22. <td>{item.id}</td>
  23. <td>{item.name}</td>
  24. </tr>
  25. ))
  26. }
  27. </tbody>
  28. </table>
  29. <nav>
  30. <ul className='pagination'>
  31. {
  32. new Array(totalPage).fill(0).map((item,index)=>(
  33. <li key={index}><button
  34. onClick={()=>setOptions({...options,currentPage:index+1})}
  35. className='btn btn-primary'
  36. >{index+1}</button></li>
  37. ))
  38. }
  39. </ul>
  40. </nav>
  41. </>
  42. )
  43. }

1-3、hooks/useRequest.js

  1. import { useState, useEffect } from "react";
  2. /*
  3. * useRequest 自定义Hook 用来请求远程接口
  4. * @params url String
  5. * @return Array [data,options,setOptions]
  6. */
  7. function useRequest(url,initOptions) {
  8. let [options, setOptions] = useState(Object.assign({
  9. currentPage: 1,
  10. pageSize: 5,
  11. },initOptions));
  12. let [data, setData] = useState({
  13. totalPage: 0,
  14. list: [],
  15. });
  16. // 调用接口,返回数据
  17. function getData() {
  18. let { currentPage, pageSize } = options;
  19. fetch(`${url}?currentPage=${currentPage}&pageSize=${pageSize}`)
  20. .then((res) => res.json())
  21. .then((res) => {
  22. setData({ ...res });
  23. });
  24. }
  25. //* 依赖项 options 或者 url发生变化 会重新发送请求/
  26. useEffect(getData, [options, url]);
  27. return [data, options, setOptions];
  28. }
  29. export default useRequest;

1-4、servies.js

  1. const express = require('express')
  2. const cors = require('cors')
  3. const logger = require('morgan')
  4. const app = express()
  5. app.use(cors())
  6. app.use(logger('dev'))
  7. // http://localhost:8000/api/users?currentPage=1&pageSize=5
  8. app.get('/api/users',function(req,res){
  9. let currentPage = parseInt(req.query.currentPage) // 1 , 2, 3
  10. let pageSize = parseInt(req.query.pageSize) // 5
  11. let total = 25;
  12. let list = []
  13. let offset = (currentPage-1) * pageSize
  14. for(let i = offset; i < offset+pageSize; i++){
  15. list.push({id:i+1, name:'name'+(i+1)});
  16. }
  17. res.json({
  18. currentPage,
  19. pageSize,
  20. totalPage:Math.ceil((total/pageSize)),
  21. list
  22. })
  23. })
  24. app.listen(8000,function(){
  25. console.log('app 启动起来了')
  26. })

2、useDrag

2-1、hooks/useDrag.js

  1. import { useState,useEffect, useLayoutEffect, useRef } from 'react';
  2. function useDrag(){
  3. // DOM位置 useRef 保存对象 可以在组件多次渲染的时候 保持不变
  4. const positionRef = useRef({
  5. currentX:0,
  6. currentY:0,
  7. lastX:0,
  8. lastY:0
  9. })
  10. const domRef = useRef(null) // domRef.current = div真是的DOM元素
  11. const [, forceUpdate ] = useState({})
  12. useLayoutEffect(() => { // 执行时机更早 想尽快的绑定事件 useEffect 什么时候绑定就不一定了
  13. let startX,startY; // 拖拽开始的x、y坐标
  14. const start = function(event){
  15. const { clientX, clientY } = event.targetTouches[0];
  16. startX = clientX;
  17. startY = clientY;
  18. domRef.current.addEventListener('touchmove',move)
  19. domRef.current.addEventListener('touchend',end)
  20. }
  21. const move = function(event){
  22. const { clientX , clientY } = event.targetTouches[0];
  23. positionRef.current.currentX = positionRef.current.lastX + (clientX - startX)
  24. positionRef.current.currentY = positionRef.current.lastY + (clientY - startY)
  25. forceUpdate({})
  26. }
  27. const end = function(){
  28. positionRef.current.lastX = positionRef.current.currentX
  29. positionRef.current.lastY = positionRef.current.currentY
  30. domRef.current.removeEventListener('touchmove',move)
  31. domRef.current.removeEventListener('touchend',end)
  32. }
  33. domRef.current.addEventListener('touchstart',start)
  34. }, [])
  35. // 让那个dom元素进行移动
  36. let style = {x: positionRef.current.currentX, y:positionRef.current.currentY}
  37. return [style,domRef]
  38. }
  39. export default useDrag;

2-2、 pages/Drag.js

  1. import React from "react";
  2. import useDrag from "../hooks/useDrag";
  3. const basicStyle = { width: "100px", height: "100px", borderRadius: "50%" };
  4. // 自定义本质 是逻辑的复用,而非数据的复用
  5. export default function Drag() {
  6. const [style1, dragRef1] = useDrag();
  7. const [style2, dragRef2] = useDrag();
  8. return (
  9. <>
  10. <div
  11. ref={dragRef1}
  12. style={{
  13. ...basicStyle,
  14. background: "red",
  15. transform: `translate(${style1.x}px,${style1.y}px)`
  16. }}
  17. ></div>
  18. <div
  19. ref={dragRef2}
  20. style={{
  21. ...basicStyle,
  22. background: "yellow",
  23. transform: `translate(${style2.x}px,${style2.y}px)`,
  24. }}
  25. ></div>
  26. </>
  27. );
  28. }

3、useForm

3-1、hooks/useForm.js

  1. import { useState } from 'react';
  2. //useRef只能放在函数里
  3. function useForm(initialValues) {
  4. const [formData, setFormData] = useState(initialValues);
  5. const setFormValue = (key, value) => {
  6. setFormData({ ...formData, [key]: value });
  7. }
  8. const resetFormValues = () => {
  9. setFormData(initialValues);
  10. }
  11. return [formData, setFormValue, resetFormValues];
  12. }
  13. export default useForm;

3-2、page/Form.js

  1. import React,{useCallback} from "react";
  2. import useForm from '../hooks/useForm'
  3. function Form(){
  4. const [ formData, setFormValue, resetFormValues ] = useForm({username:'',email:''})
  5. const changeUsername = useCallback((e)=>setFormValue('username',e.target.value),[setFormValue])
  6. const changeEmail = useCallback((e)=>setFormValue('email',e.target.value),[setFormValue])
  7. return (
  8. <form>
  9. <div className="form-group">
  10. <label>用户名</label>
  11. <input className="form-control" placeholder="用户名"
  12. value={formData.username}
  13. onChange={changeUsername}
  14. />
  15. </div>
  16. <div className="form-group">
  17. <label>邮箱</label>
  18. <input className="form-control" placeholder="邮箱"
  19. value={formData.email}
  20. onChange={changeEmail}
  21. />
  22. </div>
  23. <button type="button" className="btn btn-primary" onClick={()=>console.log(formData)}>提交</button>
  24. <button type="button" className="btn btn-primary" onClick={()=>resetFormValues()}>重置</button>
  25. </form>
  26. )
  27. }
  28. export default Form;

4、useAnimation

4-1、hooks/useAnimation.js

  1. import { useState } from "react";
  2. function useAnimate(baseClassName, activeClassName) {
  3. let [className, setClassName] = useState(baseClassName);
  4. const toggle = () => {
  5. if (className === baseClassName) {
  6. setClassName(`${baseClassName} ${activeClassName}`);
  7. } else {
  8. setClassName(`${baseClassName}`);
  9. }
  10. };
  11. return [className, toggle];
  12. }
  13. export default useAnimate;

4-2、page/Animate.js

  1. import React from "react";
  2. import useAnimation from "../hooks/useAnimation";
  3. import "./Animation.css";
  4. export default function Animation() {
  5. const [className, toggle] = useAnimation("circle", "active");
  6. return <div className={className} onClick={toggle}></div>;
  7. }

4-3、page/Animate.css

  1. .circle {
  2. width: 200px;
  3. height: 200px;
  4. transition: all 3s;
  5. border-radius: 50%;
  6. background: #ff0;
  7. }
  8. .circle.active {
  9. background: #f00;
  10. }

5、useDebounce

防抖setState

  1. import { useEffect, useState } from "react";
  2. function useDebounce(value: any, delay = 300) {
  3. const [debouncedValue, setDebouncedValue] = useState(value);
  4. useEffect(() => {
  5. const handler = window.setTimeout(() => {
  6. setDebouncedValue(value);
  7. }, delay);
  8. return () => {
  9. clearTimeout(handler);
  10. };
  11. }, [value, delay]);
  12. return debouncedValue;
  13. }
  14. export default useDebounce;

6、useClickOutside

点击目标元素外面区域触发事件

  1. import {RefObject,useEffect} from 'react'
  2. function useClickOutside(ref: RefObject<HTMLElement>,handler:Function) {
  3. useEffect(() => {
  4. const listener = (event:MouseEvent) => {
  5. if(!ref.current || ref.current.contains(event.target as HTMLElement)){
  6. return
  7. }
  8. handler(event)
  9. }
  10. window.addEventListener('click',listener)
  11. return () => {
  12. window.removeEventListener('click',listener)
  13. }
  14. }, [ref, handler])
  15. }
  16. export default useClickOutside