react-hooks 一般写法汇总

news/2024/7/15 18:08:11 标签: react.js, javascript, 前端

文件一般写法

// 引入统一封装api请求
import { getById } from "@/api";
// 引入ui组件库
import { Toast } from "antd-mobile";
// useEffect 类似vue中watch,或者moundted生命周期,视第二参数数据而定
// useState 是vue2的data、是vue3的ref或reactive,总之是动态绑定数据,用于视图使用
// useMemo 用于防止子组件随父组件变动而实时刷新,尤其是无畏刷新
import { useEffect, useState, useMemo } from "react";
// useSearchParams,查找路由后面?跟的数据
// useLocation,获取路由pathname
import { useSearchParams, useLocation } from "react-router-dom";
// 引入样式,如果以module方式命名,类似于vue中的scss scoped
import Styles from './index.module.less'
import '@/layout/navbar/index.less'
// 自用组件引入
import PvForm from "@/components/PvForm";
// searchRoute搜索路由总体参数(包含meta等)
import {searchRoute} from "@/router/utils/guard.jsx";
// rootRouter函数式路由 汇总
import {rootRouter} from "@/router/index.jsx";
const Station = (props) => {
  // 获取路由名
  const { pathname } = useLocation()
  const route = searchRoute(pathname, rootRouter)
  // * 获取自定义的路由配置
  const { nav } = route.meta
  // 记录当前滚动条距离
  const [scrollTop, setScrollTop] = useState(0);
  // 使用路由后? 跟随的参数
  const [searchParams, setSearchParams] = useSearchParams();
  // 传递组件参数 初始化	
  const [ tdInfo, setTdInfo] = useState({
    title: '土地资质',
    list: [
      { 
        name: 'projectReportImage',
        label: '项目可研报告', 
        type: 'text', 
        value: '', 
      },
    ],
  })

  // api接口获取信息处理
  const getDetail = (params, routeParams = { mode: 'YX'}) => {
	// 获取信息
    getById(params).then(res => {
      const { success, result, error} = res
      if (success) {
        judgeBuild(result); // 存储信息 
      } else {
        Toast.show({
          content: error || '信息获取失败'
        });
      }
    })
  }

// 判断是否存在
  const judgeBuild = (res) => {
    const { projectType } = res
    const isTdInfo = tabList.some(item => item.label === 'tdInfo')
    // 特定性逻辑处理
    if (['PUB_BUILD'].includes(projectType) && !isTdInfo) {
      const index = tabList.findIndex(item => item.label === 'information')
      tabList.splice(index+1, 0, {
        name: '资质',
        label: 'tdInfo'
      })
      setTabList(tabList)

      // 设置信息
      const { list } = tdInfo
      const { stationCode } = res
      const projectReportImage = res.projectReportImage ? `报告-${stationCode}` : '无'
      const landImage = res.landImage ? `性质-${stationCode}` : '无'
      const landContractImage = res.landContractImage ? `公示-${stationCode}` : '无'
      const noImpactImage = res.noImpactImage ? `证明-${stationCode}` : '无'
      const loadReportImage = res.loadReportImage ? `报告-${stationCode}` : '无'
      Object.assign(commObj, { projectReportImage, landImage, landContractImage, noImpactImage, loadReportImage })
      list.forEach(item => {
        item.value = commObj[item.name]
      })
      setTdInfo({...projectInfo, list})
    }
  }
  
  // 监听函数
  const handleScroll = (e) => {
    // 监听div内滚动条距离顶部距离
    if(e && e.target.scrollTop) {
      setScrollTop(e.target.scrollTop)
    }
  }
  // * 渲染后加载 useEffect
  useEffect(() => {
  	// 作接口请求
    const { stationId } = props
    const routeParams = searchParams.get("mode");
    getDetail({stationId},routeParams)

    // 监听滚动事件
    window.addEventListener("scroll", handleScroll, true);
    handleScroll()
    // 销毁滚动监听事件 return中的执行 (同 vue2 beforeDestroy、vue3 onBeforeUnmount)
    return () => window.removeEventListener("scroll", handleScroll, false);
  }, []); // useEffect 第二参数,确定其重复次数 为空数组时,仅执行一次(同 vue2 mounted vue3 onMounted)
 // 若无参数,则每次重绘都会执行(同 vue2 updated、vue3 onUpdated),若有参数则,参数变化才会变(同vue的watch监听)
// 使用useMemo 对子组件进行防止无用更新处理。第二个参数 是参数变换 才进行重新渲染标志
const projectInfoDom = useMemo(() => <PvForm tdInfo={tdInfo}/>, [tdInfo]);

  return (
      <>
          <div className={`${Styles.content} w-full`}>
              {scrollTop < 15 && <div className={`${Styles.head} w-full navbar ${nav}`}></div>}
              <div className={`${Styles.bottom}`} style={{padding: '0 12px'}}>
                {projectInfoDom}
                <div style={{marginTop: '10px'}}>
                  {projectInfoDom}
                </div>
              </div>
          </div>
      </>
  )
}

export default Station
.navbar {
  border-bottom: 0;
}
.content {
  background: #F7F8FA;
  min-height: calc(100vh - 45px);
  padding-bottom: 48px;
  .head {
    height: 36px;
    position: absolute;
    top: 40px;
    border-bottom: 0;
  }
  .bottom {
    position: relative;
    z-index: 1;
  }
}

组件一般写法

import { useEffect, useState } from "react";
import PvImageViewer from "@/components/PvForm/PvImageViewer"
import PvConfTable from "@/components/PvForm/PvConfTable"
import PvTimeLine from "@/components/PvForm/PvTimeLine"
import "./index.less"
import noData from "@/assets/img/noData.png"

const PvForm = (props) => {
  // console.log('pvForm', props)
  const [ tab, setTab ] = useState({
    index: 0,
    prop: props?.default || props?.prop // 适配tab项,如果没有tab 默认获取prop展示
  })
  const chooseTab = (index, item) => {
    setTab({index, prop: props[item.label] || {list:[]} })
    props.communication(index, item)
  }
  const isEmpty = (a) => {
    if (['', null, undefined].includes(a)) return true        
    if (Array.prototype.isPrototypeOf(a) && a.length === 0 ) return true; //检验空数组
    if (Object.prototype.isPrototypeOf(a) && Object.keys(a).length === 0 ) return true;  //检验空对象
    return false;
  }
  return (
      <>
          <div className="content w-full">
              { props.tabList && 
                props.tabList.length && 
                <div className='tab-list flex'>
                  {props.tabList.map((item, index) => {
                    return <div 
                              className={`tab-item ${tab.index === index ? 'tab-item-active' : ''}`} 
                              key={item.name} 
                              onClick={() => chooseTab(index, item)}
                            >
                              {item.name}
                            </div>
                  })}
                </div>
              }
              {tab.prop.title && <div className="title">{tab.prop.title}</div>}
              {tab.prop.list && 
               tab.prop.list.every(item => isEmpty(item.value)) && 
               <div className="flex flex-col items-center w-full">
                <img style={{width: '200px', height: '200px'}} src={noData}/>
                <div style={{color: "#999999"}}>暂无信息</div>
               </div>
               }
              <div className="items">
                {tab.prop.list.map((item, index) => {
                  if (!isEmpty(item.value)) {
                    if (['text'].includes(item.type)) {
                      return <div className="form-item flex" key={item.name}>
                        <div className='item-label'>{item.label}</div>
                        <div className="item-value">{item.value}</div>
                        <div className="item-unit">{item.unit}</div>
                      </div>
                    }
                    if (['upload'].includes(item.type)) {
                      return <div className="form-item" key={item.name}>
                      <div className='item-label'>{item.label}</div>
                      <div className="item-value">
                        <PvImageViewer list={item.value}></PvImageViewer>
                      </div>
                      <div className="item-bottom-line"></div>
                    </div>
                    }
                    if (['uploads'].includes(item.type)) {
                      return item.value.map(it => {
                        return <div className="form-item" key={it.name}>
                          <div className='item-label'>{it.label}</div>
                          <div className="item-value">
                            <PvImageViewer list={it.value}></PvImageViewer>
                          </div>
                        </div>
                      })
                    }
                    if (['table'].includes(item.type)) {
                      return <div key={index}>
                        {item.label && <div className="title">{ item.label }</div>}
                        {item.value.map((it, ind) => {
                          return <PvConfTable list={it} key={ind}/>
                        })}
                      </div>
                    }
                    if (['timeline'].includes(item.type)) {
                      return <div key={index}>
                        {item.label && <div className="title">{ item.label }</div>}
                        <PvTimeLine list={item.value}></PvTimeLine>
                      </div>
                    }
                  }
                })}
              </div>
          </div>
      </>
  )
}

export default PvForm
.content {
  background: #ffffff;
  border-radius: 8px;
  padding: 16px 0 20px 0;
  // font-size: var(--adm-font-size-7);
  .tab-list {
    margin: 17px 15px 16px 15px;
    .tab-item {
      flex: 1;
      height: 32px;
      line-height: 32px;
      background: #2283e21a;
      font-size: 12px;
      color: #2283E2;
      border: 1px solid #2283e21a;
      text-align: center;
      &:nth-child(1) {
        border-radius: 4px 0px 0px 4px;
      }
      &:nth-last-child(1) {
        border-radius: 0px 4px 4px 0px;
      }
    }
    .tab-item-active {
      background: #2283E2;
      border: 1px solid #2283E2;
      color: #ffffff;
    }
  }
  .title {
    font-size: 14px;
    font-weight: bold;
    color: #323233;
    padding: 0 0 12px 0;
    margin: 0 15px 0 15px;
    border-bottom: 1px solid #EBEDF0;
    &:nth-child(n+2) {
      margin-top: 16px;
    }
  }
  .form-item {
    margin-top: 12px;
    padding: 0 15px;
    font-size: 14px;
    color: #C8C9CC;
    .item-label {
      width: 90px;
    }
    .item-bottom-line {
      margin-top: 12px;
      height: 1px;
      background: #EBEDF0;
    }
    .item-value {
      color: #323233;
      flex: 1;
    }
  }
}

图片preview 组件 写法示例

import { useEffect, useState } from "react";
import { ImageViewer } from "antd-mobile";
import "./index.less"

const PvImageViewer = (props) => {
  const [visible, setVisible] = useState(false)
  const [imageSrc, setImageSrc] = useState(null)
  const imageView = (src) => {
    setImageSrc(src)
    setVisible(true)
  }
  return (
      <>
          <div className="image-view flex flex-wrap items-baseline">
            {
              props.list &&
              props.list.length &&
              props.list.map((item, index) => {
               return <div className="image-item flex flex-col align-center justify-center" style={{width: '30%'}} onClick={ () => imageView(item.imgSrc) } key={index}>
                  <img className="w-full" style={{ objectFit: 'cover', height: '26vw' }} src={item.imgSrc + '?x-oss-process=image/resize,m_lfit,h_200,w_200'} alt="" />
                  <div className="image-name">{item.imgName}</div>
                </div>
              })
            }
            <ImageViewer
              image={imageSrc}
              visible={visible}
              onClose={() => {
                setVisible(false)
              }}
            />
          </div>
      </>
  )
}

export default PvImageViewer
.image-view {
  .image-item {
    margin-top: 8px;
    &:nth-child(3n+2) {
      margin-left: 15px;
    }
    &:nth-child(3n+3) {
      margin-left: 15px;
    }
    .image-name {
      font-size: 10px;
      margin-top: 6px;
      color: #969799;
      text-align: center;
      display: block;
    }
  }
}

http://www.niftyadmin.cn/n/4961294.html

相关文章

【MyBatis】动态SQL > 重点:${...}和#{...}与resultMap和resultType的区别

目录 一、MyBatis动态sql 1.1 动态sql的作用 1.2 动态sql作用论证 1.2.1 条件判断&#xff1a;<if> 1.2.2 循环迭代&#xff1a;<foreach> 1.2.3 SQL片段重用 1.2.4 动态条件组合&#xff1a;<choose><when><otherwise> 1.2.5 <where…

chatglm llm实时流api接口及post访问

参考&#xff1a; https://github.com/THUDM/ChatGLM-6B/pull/573/commits/02947052eefe392fd9f9632894e9551a805c6109 https://github.com/THUDM/ChatGLM-6B/pull/573 1、代码&#xff1a; 提前安装&#xff1a; sse_starlette、fastapi python stream_api.pystream_api.p…

Java-Maven-解决maven deploy时报 401 Reason Phrase Unauthorized 错误

Java-Maven-解决maven deploy时报 401 Reason Phrase Unauthorized 错误 环境 Java JDK 1.8Maven 3.3.9 引言 项目需要打成jar包上传到私服&#xff0c;供其它项目引用。此时需要执行 mvn clean deploy 命令产生 401 错误。 解决401错误 报错信息 执行命令&#xff1a;m…

考研C语言进阶题库——更新51-60题

目录 51.银行系中有很多恒星&#xff0c;H 君晚上无聊&#xff0c;便爬上房顶数星星&#xff0c;H 君将整个银河系看做一个平面&#xff0c;左上角为原点&#xff08;坐标为&#xff08;1, 1&#xff09;&#xff09;。现在有 n 颗星星&#xff0c;他给每颗星星都标上坐标&…

python批量做网卡bond

python脚本获取服务器网卡的MAC地址和本机IP #!/usr/bin/pythonimport fcntl,socket,structdef getHwAddr(ifname):s socket.socket(socket.AF_INET, socket.SOCK_DGRAM)info fcntl.ioctl(s.fileno(), 0x8927, struct.pack(256s, ifname[:15]))return :.join([%02x % ord(ch…

R语言05-R语言中的数据框

概念 在R语言中&#xff0c;数据框&#xff08;Data Frame&#xff09;是一种类似于表格的数据结构&#xff0c;用于存储二维数据&#xff0c;其中每列可以包含不同类型的数据&#xff0c;例如数值、字符、因子、逻辑等。数据框是R中常用的数据结构&#xff0c;用于处理和分析…

二级评论列表功能

一&#xff1a;需求场景 我的个人网站留言列表在开发时&#xff0c;因为本着先有功能的原则。留言列表只有一级&#xff0c;平铺的。 当涉及多人回复&#xff0c;或者两个人多次对话后&#xff0c; 留言逻辑看着非常混乱。如下图 于是&#xff0c;我就打算将平铺的列表&#…

代码随想录算法训练营第四十二天 | 01背包问题,01背包问题(滚动数组),416. 分割等和子集

代码随想录算法训练营第四十二天 01背包问题01 背包二维dp数组01背包 01背包问题(滚动数组)416. 分割等和子集 01背包问题 视频讲解 以下是几种背包&#xff0c;如下&#xff1a; 至于背包九讲其其他背包&#xff0c;面试几乎不会问&#xff0c;都是竞赛级别的了&#xff0c;…