react18之08自定义hook (简单的axios-get、修改浏览器title、localStorage、获取滚动条位置、img转换为base64、金额、挂载卸载更新)

react18之自定义hook ()

  • hook的使用规则
    • 自定义hook本质而言就是一个函数,也就是抽离公共部门的代码,类似抽离组件或者说mixin(vue中的mixin)。
    • hook必须以use开头(不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则)
    • 内部正常使用useState useEffect或者其他hooks
    • 自定义返回结果,格式不限
    • 在两个组件中使用相同的 Hook 不会共享 state(自定义 Hook 是一种重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是完全隔离的。)
    • 每次调用 Hook,它都会获取独立的 state(从 React 的角度来看,我们的组件只是调用了 useState 和 useEffect,一个组件中可以调用多次useState和useEffect,它们都是完全独立的)

01:自定义一个 简单的axios hook 发起get请求

  • npm i axios

useHttp.jsx

import {
    
     useState, useEffect } from "react";
import axios from "axios";
export default function UseHttp(props) {
    
    
  const {
    
     url } = props;
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    
    
    setLoading(true);
    axios.get(url)
      .then((res) => {
    
    
        setData(res);
      })
      .catch((err) => {
    
    
        setError(err);
      })
      .finally(() => {
    
    
        setLoading(false);
      });
  }, [url]);
  return [loading, data, error];
}

使用useHttp hook

import React, {
    
     useState,useEffect } from 'react';
import UseHttp from './useHttp';
export default function Base(props) {
    
    
    const [list,setList] = useState([])
    const url = 'http://localhost:9999' + '/list'
    const [loading,data,error] = UseHttp({
    
    url})
    useEffect(()=>{
    
    
      if ( data && data.status === 200) {
    
    
        setList(data.data)
      }
    },[data])

    return (
        <div className='content'>
          {
    
    
            loading ? '加载中' : 
            <>
             {
    
     error ? <div>error</div> :
              <div>
                {
    
    
                  list.map(item=>{
    
    
                    return (
                      <div key={
    
    item.id}>
                        name-{
    
     item.name};
                        age- {
    
     item.age }
                      </div>
                    )
                  })
                }
              </div>}
            </>
          }
        </div>
    )
}

效果

在这里插入图片描述

02:自定义一个 修改浏览器title hook

import {
    
     useEffect } from 'react';

export default function useTitle(props) {
    
    
    const {
    
    title} = props

    useEffect(() => {
    
    
        document.title = title
        // 组件卸载时,恢复默认标题
        return () => {
    
    
          document.title = 'Original Title';
        };
    }, [title])

    return {
    
     title }
}

03:自定义一个 localStorage(获取、存储、移除) hook

useLocalStorage.jsx

import {
    
     useState, useEffect } from 'react';

const useLocalStorage = (key, initialValue) => {
    
    
  // 根据key获取localStorage的数据,若是有数据则返回,没有则返回初始化设置的值
  const [data, setData] = useState(() => {
    
    
    let storageVal = localStorage.getItem(key);
    return (storageVal && storageVal !== 'undefined') ? JSON.parse(storageVal) : initialValue;
  });

  // 若是数据有变化 则存储数据
  useEffect(() => {
    
    
    localStorage.setItem(key, JSON.stringify(data));
  }, [key, data]);

  const removeLocalStorage = () => {
    
    
    setData(initialValue);
    localStorage.removeItem(key);
  };

  return [data, setData, removeLocalStorage];
};

export default useLocalStorage;

使用hook

import React, {
    
     useState, useEffect } from 'react';
import useLocalStorage from './useLocalStorage';
export default function Base(props) {
    
    
    const [name,setName,removeLocalStorage]= useLocalStorage('name','')
    function getName(){
    
    
      console.log('name',name);
    }
    function setLocalName(){
    
    
      setName('我是setName')
    }
    function delLocalName(){
    
    
      removeLocalStorage('name')
    }
    return (
        <div className='content'>
          <div>
            <div>name-{
    
    name}</div>
            <div>获取name数据 - <button onClick={
    
    getName}>getName</button></div>
            <div>设置name数据 - <button onClick={
    
    setLocalName}>setName</button></div>
            <div>移除name数据 - <button onClick={
    
    delLocalName}>delName</button></div>
          </div>
        </div>
    )
}

效果

在这里插入图片描述

04:自定义一个 useScrollPosition(获取当前滚动条的位置) hook

useScrollPosition.jsx

import {
    
     useState, useEffect } from 'react';
function useScrollPosition() {
    
    
  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    
    
    const handleScroll = () => {
    
    
      let scrollY = window.scrollY ? Math.round(window.scrollY) : 0
      setScrollPosition(scrollY);
    }
    document.addEventListener("scroll", handleScroll);

    return () => {
    
    
      document.removeEventListener("scroll", handleScroll)
    }
  }, []);

  return scrollPosition;
}

export default useScrollPosition;

使用

   const scrollPosition = useScrollPosition()
   console.log('scrollPosition', scrollPosition);

效果

在这里插入图片描述

05:自定义一个 useImageToBase64(img转换为base64) hook

useImageToBase64.jsx

import React, {
    
     useState } from 'react';

const useImageToBase64 = () => {
    
    
  const [base64Image, setBase64Image] = useState(null);

  const imageToBase64 = (file) => {
    
    
    return new Promise((resolve, reject) => {
    
    
      const reader = new FileReader();

      reader.onload = () => {
    
    
        resolve(reader.result);
      };

      reader.onerror = (error) => {
    
    
        reject(error);
      };

      reader.readAsDataURL(file);
    });
  };

  const handleImageUpload = (event) => {
    
    
    const file = event.target.files[0];
    if (file) {
    
    
      imageToBase64(file)
        .then((base64) => {
    
    
          setBase64Image(base64);
        })
        .catch((error) => {
    
    
          console.error('Error converting image to Base64:', error);
        });
    }
  };

  return {
    
     base64Image, handleImageUpload };
};

export default useImageToBase64;

使用

import React from 'react';
import useImageToBase64 from "../自定义hook/useImageToBase64 "
export default function Test(props) {
    
    
  const {
    
     base64Image, handleImageUpload } = useImageToBase64();
  console.log('base64Image',base64Image);
    return (
        <div className='content' style={
    
    {
    
    marginTop:'40px'}}>
          Test
          <div>
            <input type="file" accept="image/*" onChange={
    
    handleImageUpload} />
            {
    
    base64Image && <img src={
    
    base64Image} alt="Uploaded" />}
          </div>
        </div>
    )
}

效果

在这里插入图片描述

06:自定义一个处理金额,保留位数的hook

hook.js

import {
    
     useEffect, useState } from 'react';

// 处理金额的小数点
const useFormattedDecimal = (val, decimal) => {
    
    
  const [formattedDecimal, setFormattedDecimal] = useState('');

  useEffect(() => {
    
    
    const formatDecimal = (value, decimalPlaces) => {
    
    
      try {
    
    
        const decimalVal = decimalPlaces || 0;
        const str = `${
      
      Number(value).toFixed(decimalVal)}`;
        if (decimalVal === 0) {
    
    
          return str.replace(/\B(?=(?:\d{3})+$)/g, ',');
        }
        // 取到整数部分
        const intSum = str.substring(0, str.indexOf('.')).replace(/\B(?=(?:\d{3})+$)/g, ',');
        // 取到小数部分
        const dot = str.substring(str.length, str.indexOf('.'));
        return intSum + dot;
      } catch (e) {
    
    
        return 0;
      }
    };

    setFormattedDecimal(formatDecimal(val, decimal));
  }, [val, decimal]);

  return formattedDecimal;
};

export {
    
    
  useFormattedDecimal
};

使用hook


import React from 'react';
import {
    
     useFormattedDecimal } from "./hook";
export default function App(props) {
    
    
  return (
    <div className='content'>
      count - {
    
     useFormattedDecimal(111.199,2) } <br/>
      count2 - {
    
     useFormattedDecimal(12345.2344,3) } <br/>
      count3 - {
    
     useFormattedDecimal(999333.35,2) } <br/>
      count4 - {
    
     useFormattedDecimal(18112833.652,2) }
    </div>
  );
}

效果

在这里插入图片描述

07:挂载、卸载、更新effect hook封装

hook.js

import {
    
     useEffect, useRef, useState } from "react";

// 挂载时候触发
const useMount = (callback) => {
    
    
  useEffect(callback, []);
};

// 卸载时候触发
const useUnMount = (callback) => {
    
    
  useEffect(() => {
    
    
    return () => callback();
  }, []);
};

// 依赖更新时候触发
const useUpdateEffect = (effect, deps) => {
    
    
  const isFirstRender = useRef(true); // 判断是否第一次渲染
  useEffect(() => {
    
    
    if (isFirstRender.current) {
    
    
      isFirstRender.current = false;
    } else {
    
    
      return effect();
    }
  },deps);
};

export {
    
      useMount, useUnMount, useUpdateEffect };

使用 app.jsx


import React, {
    
     useState } from 'react';
import Son from "./Son";
export default function App(props) {
    
    
  const [sonShow,setSonShow] = useState(true)
  const sonShowClick = ()=>{
    
    
    setSonShow(!sonShow)
  }
  return (
    <div className='content'>
      {
    
    
        sonShow ?  <Son></Son> : 'son不展示'
      }
      <button onClick={
    
    sonShowClick}>son展示</button>
    </div>
  );
}

Son.jsx

import React, {
    
     useState } from 'react';
import {
    
     useMount, useUnMount, useUpdateEffect } from "./hook";
export default function Son(props) {
    
    
  const [count,setCount] = useState(0)
  const [name,setName] = useState('name')
  // 组件挂载hook
  useMount(()=>{
    
    
    console.log('useMount组件挂载');
  })
  // 组件卸载hook
  useUnMount(()=>{
    
    
    console.log('组件卸载时候-count',count); // 组件销毁时候 都打印出0
  })

  // 依赖更新触发hook
  useUpdateEffect(()=>{
    
    
    console.log('useUpdateEffect-count',count);
  },[count,name])

  // 修改了count也不会触发之 useMount调用 达到模拟vue的mounted钩子函数
  const addcount = ()=>{
    
    
    setCount(count+ 1)
  }
  const changeName = () => {
    
    
    setName('XXX')
  }
  return (
    <div className='content'>
      count - {
    
     count } <br/>
      <button onClick={
    
    addcount}>addcount</button><br/>
      name - {
    
    name}<br/>
      <button onClick={
    
    changeName}>changeName</button>
    </div>
  );
}

08 useInput数据双向绑定hook

hook.js

// 数据双向绑定input
const useInput = (initVal = '') => {
    
    
  const [value,setValue] = useState(initVal)
  const inputChange = (event) => {
    
    
    setValue(event.target.value)
  }
  return {
    
    
    value,
    onChange: inputChange
  }
}

export {
    
     useInput };

使用


import React, {
    
     useEffect } from 'react';
import {
    
     useInput } from './hook';
export default function App(props) {
    
    
  const nameValue = useInput('')
  // 使用 useEffect 监听input值发生变化
  useEffect(()=>{
    
    
    console.log('nameValue',nameValue.value);
  },[nameValue.value])
  return (
    <div className='content'>
      <input type="text" value={
    
    nameValue.value} onChange={
    
    nameValue.onChange}/>
      nameValue -{
    
    nameValue.value}
    </div>
  );
}

09 useToggle切换flag hook

hook.js

// useToggle
const useToggle = (initFlag = false) =>{
    
    
  const [flag,setFlag] = useState(initFlag)
  const toggle = () => {
    
    
    setFlag((preVal)=> !preVal)
  }
  return [
    flag,
    toggle
  ]
}

export {
    
     useToggle };

使用useToggle

import React from 'react';
import {
    
     useToggle } from "./hook";
export default function App(props) {
    
    
  const [flag,setFlag] = useToggle(false)
  return (
    <div className='content'>
      <button onClick={
    
     setFlag  }>fffff</button>
      {
    
    
        flag ? '1' : '0'
      }
    </div>
  );
}

000使用第三方hook库

简单的使用全屏


import React, {
    
     useRef } from 'react';
import {
    
     useFullscreen, useToggle } from 'react-use';
export default function App(props) {
    
    
  const dom = useRef(null)
  const [show, toggle] = useToggle(false);
  useFullscreen(dom, show, {
    
     onClose: () => toggle(false) });
  return (
    <div className='content'>
      {
    
    
        show ? <div ref={
    
    dom} style={
    
    {
    
     color: "red",background: "#fff" }}>
        我是可以全屏的div
        <button onClick={
    
     () => toggle()}>退出全屏</button>
      </div> : ''
      }
      <button onClick={
    
     () => toggle()}>全屏</button>
    </div>
  );
}

猜你喜欢

转载自blog.csdn.net/weixin_43845137/article/details/132123373