当前位置: 首页 > news >正文

React+Ant design

文章目录

    • React
      • 1. 样式 className
      • 2. React 常用 Hooks
        • 1. useState
        • 2. useEffect
        • 3. useMemo
        • 4. useCallback
        • 5. useRef —— 组件的 “持久化抽屉”
        • 6. useContext——组件的 “共享公告板”
        • 7. useReducer—— 复杂状态的 “管理经理”
      • 3. 判断语句
      • 4. 事件点击
      • 4. 父子组件传递
        • 1. 父组件
        • 2. 子组件
    • AntDesgin
      • modal弹窗
      • Tree 树

React

语法返回,得添加括号,例如:

return ({<><span></span><span></span></>
})

1. 样式 className

样式书写

  1. className
  2. style={{width:'100vh',height:'200px'}}
  3. 样式定义为变量【建议】
const styleObj = {width:'100vh',height:'200px'}
style = {styleObj}

在这里插入图片描述

创建
npx create-react-app my-react-app 【推荐】或npx create-next-app@latestnpm create vite@latest my-react-app -- --template react

2. React 常用 Hooks

1. useState
import React,{ useState } from 'react'function StateFunction () {const [name, setName] = useState('函数')return (<div onClick={ () => setName('我使用hooks变成这样了') }>//	setName也可以写入方法,如setName( val => val+'xxxx' )这是一个函数式组件————{name}</div>)
}export default StateFunction
2. useEffect
  • useEffect又称副作用hooks。

  • 作用:比如请求数据、设置定时器、操作 DOM 等。

  • 执行时机:在渲染结束之后执行

  • 什么是副作用?

    • 副作用 ( side effect ): 数据获取,数据订阅,以及手动更改 React 组件中的 DOM 都属于副作用
    • 因为我们渲染出的页面都是静态的,任何在其之后的操作都会对他产生影响,所以称之为副作用
  • 使用:

    • 1.第一个参数,接收一个函数作为参数
    • 2.第二个参数,接收【依赖列表】,只有依赖更新时,才会执行函数
    • 3.返回一个函数,先执行返回函数,再执行参数函数
function UserProfile() {const [user, setUser] = useState(null);// 组件一加载就请求用户数据(类似“订位”)useEffect(() => {// 发起请求(副作用操作)fetch('https://api.example.com/user').then(res => res.json()).then(data => setUser(data));// 组件消失时清理(类似“买单”)return () => {// 比如清除定时器、取消请求等};}, []); // 空数组表示:只在组件第一次加载和卸载时执行useEffect( () => {console.log('2222函数式组件结束渲染')},[num])useEffect( () => {console.log('2222函数式组件结束渲染')},[num,val])return <div>{user ? user.name : '加载中...'}</div>;
}
3. useMemo

使用useMemo可以传递一个创建函数和依赖项,创建函数会需要返回一个值,只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值。简单来说,作用是让组件中的函数跟随状态更新(即优化函数组件中的功能函数)。

const [num, setNum] = useState(1)
const [age, setAge] = useState(18)const getDoubleNum = useMemo( () => {console.log(`获取双倍Num${num}`)return 2 * num  //	假设为复杂计算逻辑
},[num] )return (<div onClick={ () => { setAge( age => age+1 ) }  }><br></br>这是一个函数式组件————num:{  getDoubleNum }  //  注意这里没括号,因为是返回值<br></br>age的值为————{ age }<br></br></div>
)
4. useCallback
  • 在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可
  • 如果有函数传递给子组件,使用useCallback
  • 缓存一个组件内的复杂计算逻辑需要返回值时,使用useMemo
import React, { useState, useCallback } from 'react';
function Parent() {const [count, setCount] = useState(0);const [name, setName] = useState('');const handleClick = useCallback(() => {setCount(count + 1);}, [count]);return (<div><input value={name} onChange={e => setName(e.target.value)} /><Child handleClick={handleClick} /><p>Count: {count}</p></div>);
}const Child = React.memo(({ handleClick }) => {console.log('Child rendered');return <button onClick={handleClick}>Click me</button>;
});
5. useRef —— 组件的 “持久化抽屉”
  • useRef就是返回一个子元素索引,此索引在整个生命周期中保持不变。作用也就是:长久保存数据。注意事项,保存的对象发生改变,不通知。属性变更不会重新渲染,
  • 保存一个值,在整个生命周期中维持不变
  • ref保存的对象发生改变,不会主动通知,属性变更不会重新渲染
function InputFocus() {// 创建一个“抽屉”存输入框DOMconst inputRef = useRef(null);return (<div>{/* 把输入框“放进抽屉” */}<input ref={inputRef} />{/* 点击按钮时,从“抽屉”里拿出来并聚焦 */}<button onClick={() => inputRef.current.focus()}>聚焦输入框</button></div>);
}
6. useContext——组件的 “共享公告板”
  • 让组件之间 “共享数据”,避免一层层传递(比如爷爷组件给孙子组件传数据,不用经过爸爸组件)。
// 1. 创建公告板(Context)
const ThemeContext = createContext('light');// 2. 上层组件提供数据(贴公告)
function App() {return (<ThemeContext.Provider value="dark"><Navbar /> {/* 子组件 */}</ThemeContext.Provider>);
}// 3. 深层组件直接读取(看公告)
function Navbar() {// 用 useContext 直接获取公告板内容const theme = useContext(ThemeContext);return <div style={{ background: theme === 'dark' ? 'black' : 'white' }}>导航栏</div>;
}
7. useReducer—— 复杂状态的 “管理经理”
  • 状态逻辑较复杂(比如多个状态相互依赖、有多种更新方式)时,用它来统一管理,比 useState 更清晰。
  • 生活例子:小商店(简单状态)老板自己记账就行(useState);大超市(复杂状态)需要专门的会计(useReducer)来处理进货、销售、退货等多种操作,账目更清晰。
  • 注意事项:
    • useReducer接收两个参数,第一个是reducer函数,第二个是初始状态
    • reducer函数根据不同的action决定如何更新状态
    • dispatch用于发送action从而更新状态
// 1. 定义“会计规则”(reducer函数:接收当前状态和操作,返回新状态)
function cartReducer(state, action) {switch (action.type) {case 'ADD_ITEM':return [...state, action.item]; // 添加商品case 'REMOVE_ITEM':return state.filter(item => item.id !== action.id); // 移除商品default:return state;}
}function ShoppingCart() {// 2. 初始化“账本”,[当前状态, 操作函数] = useReducer(规则, 初始值)const [cart, dispatch] = useReducer(cartReducer, []);return (<div><button onClick={() => dispatch({ type: 'ADD_ITEM', item: { id: 1, name: '苹果' } })}>加入苹果</button><p>购物车:{cart.map(item => item.name).join(',')}</p></div>);
}

3. 判断语句

  • 三元表达式
<div>{selectType === 'player' ? '参赛者信息' : '设备信息'}</div>
<div className="card-header">{selectType === 'player' ? (<><span className="status">参赛中</span></>) : (<><span className="status">在线</span></>)}
</div>
<span>{playersInfo['gender'] === '男' ? (<ManOutlined className="sex-icon" />) : (<WomanOutlined className="sex-icon" />)}
</span>
  • 使用判断语句
{panelStatus !== 'off' && (<ButtonclassName="toggle-btn"type="primary"size="small"onClick={() => setInfoCollapsed(!infoCollapsed)}icon={infoCollapsed ? <DoubleLeftOutlined /> : <DoubleRightOutlined />}/>
)}
  • 使用 CSS 控制显示隐藏
<ButtonclassName={`toggle-btn ${panelStatus === 'off' ? 'hidden' : ''}`}type="primary"size="small"onClick={() => setInfoCollapsed(!infoCollapsed)}icon={infoCollapsed ? <DoubleLeftOutlined /> : <DoubleRightOutlined />}
/>.hidden {display: none;
}
  • 使用内联样式
<ButtonclassName="toggle-btn"type="primary"size="small"style={{ display: panelStatus === 'off' ? 'none' : 'inline-block' }}onClick={() => setInfoCollapsed(!infoCollapsed)}icon={infoCollapsed ? <DoubleLeftOutlined /> : <DoubleRightOutlined />}
/>

4. 事件点击

此处需要写成onClick={()=>handlePlay('live')}>而不能写成onClick={handlePlay('live')}>

const handlePlay = (key)=>{console.log('播放');setVideoStatus(key)}<button className="play-btn" onClick={()=>handlePlay('live')}></button>

4. 父子组件传递

  • 父组件向子组件传递数据:
    • 通过 props 传递基本类型数据(如字符串 message)
    • 通过 props 传递复杂类型数据(如对象 userInfo)
    • 子组件通过 props.属性名 访问这些数据
  • 父组件向子组件传递方法:
    • 父组件定义方法 handleParentMethod 并通过 props 传递给子组件
    • 子组件通过 props.parentMethod() 调用该方法,还可以传递参数
  • 子组件向父组件传递数据:
    • 父组件定义回调函数 handleChildData 并传递给子组件
    • 子组件通过调用 props.onDataChange(数据) 将数据传递给父组件
    • 父组件在回调函数中更新自己的状态,实现数据传递
1. 父组件
<import React, { useState } from 'react';
import ChildComponent from './ChildComponent';const ParentComponent = () => {// 父组件的状态const [parentMessage, setParentMessage] = useState("我是来自父组件的消息");const [childInputValue, setChildInputValue] = useState("");// 父组件的方法,将传递给子组件const handleParentMethod = (value) => {alert(`父组件接收到子组件的调用,参数:${value}`);};// 接收子组件传递的数据const handleChildData = (data) => {setChildInputValue(data);};return (<div style={{ border: '2px solid #4285f4', padding: '20px', margin: '20px',borderRadius: '8px'}}><h3>父组件</h3><p>子组件输入的值:{childInputValue}</p>{/* 向子组件传递数据和方法 */}<ChildComponent message={parentMessage} parentMethod={handleParentMethod}onDataChange={handleChildData}userInfo={{ name: "张三", age: 30 }} // 传递对象/></div>);
};export default ParentComponent;
2. 子组件
<import React, { useState } from 'react';const ChildComponent = (props) => {// 子组件的状态const [childMessage, setChildMessage] = useState("");// 调用父组件传递过来的方法const handleCallParentMethod = () => {props.parentMethod("Hello 父组件,我是子组件");};// 向父组件传递数据const handleInputChange = (e) => {const value = e.target.value;setChildMessage(value);// 通过回调函数将数据传递给父组件props.onDataChange(value);};return (<div style={{ border: '2px solid #34a853', padding: '15px', margin: '10px 0',borderRadius: '8px'}}><h4>子组件</h4>{/* 使用父组件传递的数据 */}<p>父组件传递的消息:{props.message}</p><p>父组件传递的用户信息:{props.userInfo.name}{props.userInfo.age}</p>{/* 子组件输入框,数据会传递给父组件 */}<inputtype="text"value={childMessage}onChange={handleInputChange}placeholder="输入内容将传递给父组件"style={{ padding: '8px', margin: '10px 0',width: '300px'}}/>{/* 按钮调用父组件方法 */}<button onClick={handleCallParentMethod}style={{ padding: '8px 16px',backgroundColor: '#4285f4',color: 'white',border: 'none',borderRadius: '4px',cursor: 'pointer'}}>调用父组件方法</button></div>);
};export default ChildComponent;

AntDesgin

modal弹窗

  • 居中:centered={true}
  • 取消底部按钮:footer={null}
  • 内容居中:style={{ textAlign: 'center' }}
  • 关闭销毁弹窗:destroyOnClose={true}

Tree 树

在这里插入图片描述

import { Card, Row, Col, Space, Tree, Tooltip, Modal, Button,Input } from 'antd'
import './index.scss'
import { useState, useEffect, useMemo } from 'react'
import playerList from './playerList.json'
import { beidouService } from '@/api/beidou'
const { Search } = Inputconst transformData = data => {return data.map(item => {const transformedItem = {...item,title: item.label, // 将label转换为Tree组件需要的title字段}// 递归处理子节点if (item.children && item.children.length > 0) {transformedItem.children = transformData(item.children)}return transformedItem})
}
// 节点遍历搜素
const getParentKey = (key, tree) => {let parentKey;for (let i = 0; i < tree.length; i++) {const node = tree[i];if (node.children) {if (node.children.some(item => item.key === key)) {parentKey = node.key;} else if (getParentKey(key, node.children)) {parentKey = getParentKey(key, node.children);}}}return parentKey;
};
const TreeComponent = ({ setSelectedPlayers }) => {// 状态管理const [defaultData, setDefaultData] = useState([]) // 树形结构数据// 模拟从接口获取数据(实际是从本地文件读取)useEffect(() => {// 模拟异步获取数据const fetchData = async () => {const transformedData = transformData(playerList)setDefaultData(transformedData)setSearchValue(transformedData)}fetchData()}, [])const [expandedKeys, setExpandedKeys] = useState([])const [searchValue, setSearchValue] = useState('')const [autoExpandParent, setAutoExpandParent] = useState(true)const onExpand = newExpandedKeys => {setExpandedKeys(newExpandedKeys)setAutoExpandParent(false)}const onChange = e => {const { value } = e.targetif (!value) {setExpandedKeys([]);setSearchValue('');setAutoExpandParent(true);return;}// 创建一个函数来递归查找所有匹配的节点const findMatchingNodes = (nodes, allMatchingKeys = []) => {nodes.forEach(node => {if (node.title.indexOf(value) > -1) {allMatchingKeys.push(node.key);}if (node.children && node.children.length > 0) {findMatchingNodes(node.children, allMatchingKeys);}});return allMatchingKeys;};// 查找所有匹配的节点const allMatchingKeys = findMatchingNodes(defaultData);// 为每个匹配的节点获取所有父节点const newExpandedKeys = Array.from(new Set(allMatchingKeys.flatMap(key => {const parents = [];let parentKey = getParentKey(key, defaultData);while (parentKey) {parents.push(parentKey);parentKey = getParentKey(parentKey, defaultData);}return [...parents, key];}))); setExpandedKeys(newExpandedKeys)setSearchValue(value)setAutoExpandParent(true)}const onCheck = (checkedKeys, info) => {const leafNodes = info.checkedNodes.filter(node => !node.children || node.children.length === 0)setSelectedPlayers(leafNodes)}const treeData = useMemo(() => {const loop = data =>data.map(item => {const strTitle = item.title;const index = strTitle.indexOf(searchValue);const beforeStr = strTitle.substring(0, index);const afterStr = strTitle.slice(index + searchValue.length);const title =index > -1 ? (<span key={item.key}>{beforeStr}<span className="site-tree-search-value">{searchValue}</span>{afterStr}</span>) : (<span key={item.key}>{strTitle}</span>);if (item.children) {return { title, key: item.key, children: loop(item.children) };}return {title,key: item.key,};});return loop(defaultData);}, [searchValue]);return (<div><Searchstyle={{ margin: '10px 0', width: 250 }}placeholder="搜索"onChange={onChange}/><TreeclassName="players-tree"treeData={treeData}checkableonCheck={onCheck}onExpand={onExpand}expandedKeys={expandedKeys}autoExpandParent={autoExpandParent}/></div>)
}
const ContentComponent = ({ selectedPlayers }) => {// 如果没有选中的信息,显示提示文本if (!selectedPlayers || selectedPlayers.length === 0) {return (<div className="content-info"><p style={{ textAlign: 'center', color: '#999' }}>请从左侧选择选手查看详情</p></div>)}// 显示选中的选手信息return (<div className="content-info">{selectedPlayers.map((playersInfo, index) => {return (<div className="content-info-item" key={index}><Row className="center mb1rem"><Col span={24}><h3>{playersInfo.title || '选手信息'}</h3></Col></Row><Row className="mb1rem"><Col span={12}><p className="mb1rem" style={{ display: 'inline-flex' }}><span>姓名: </span><Tooltipplacement="right"title={playersInfo['label'] || '-'}><spanstyle={{whiteSpace: 'nowrap',overflow: 'hidden',textOverflow: 'ellipsis',display: 'inline-block',maxWidth: '90px',}}>{playersInfo['label'] || '-'}</span></Tooltip></p><p className="mb1rem"><span>性别: </span><spanstyle={{color:playersInfo['性别'] === '男'? '#0eaff3': playersInfo['性别'] === '女'? '#ed08ac': 'inherit',}}>{playersInfo['性别'] || '-'}</span></p><p className="mb1rem" style={{ display: 'flex' }}><span>国籍: </span>{playersInfo['国籍'] && playersInfo['国籍'] !== '-' ? (<spanstyle={{display: 'flex',alignItems: 'center',gap: '4px',}}><imgsrc={`/src/assets/icon_country/${playersInfo['国籍']}.png`}alt={playersInfo['国籍']}style={{width: '25px',height: '20px',verticalAlign: 'middle',}}onError={e => {// 如果图标不存在,则显示文本e.target.style.display = 'none'}}/>{playersInfo['国籍']}</span>) : ('-')}</p></Col><Col span={12}><p className="mb1rem"><span>参赛号: </span>{playersInfo['参赛号'] || '-'}</p><p className="mb1rem"><span>所属大洲: </span>{playersInfo['所属大洲'] || '-'}</p></Col></Row></div>)})}</div>)
}
const VideoComponent = () => {const [isModalOpen, setIsModalOpen] = useState(false)const showModal = () => {setIsModalOpen(true)}const handleOk = () => {setIsModalOpen(false)}const handleCancel = () => {setIsModalOpen(false)}// 测试登录接口const onFinish = async () => {await beidouService.getMatchList().then(res => {console.log('res', res)})}return (<div><Button type="primary" onClick={showModal}>Open Modal</Button><Button type="primary" onClick={onFinish} style={{ marginLeft: 10 }}>测试登录接口</Button><Modaltitle="八百流沙极限赛视频"closable={{ 'aria-label': 'Custom Close Button' }}open={isModalOpen}onOk={handleOk}onCancel={handleCancel}centered={true}footer={null}style={{ textAlign: 'center' }}width={900}destroyOnHidden={true}>{isModalOpen && (<>{/* <video id="player-container-id" preload="auto" controls style={{ width: '100%', height: '600px'}}></video> */}<iframesrc="//player.bilibili.com/player.html?isOutside=true&aid=582534850&bvid=BV1S64y1M7N6&cid=172373846&p=1"border="0"allowfullscreen="true"style={{ width: '100%', height: '600px' }}></iframe></>)}</Modal></div>)
}const Players = () => {const [selectedPlayers, setSelectedPlayers] = useState([])return (<div className="players-page"><Card title="选手管理" variant="outlined"><div className="players-content"><Row gutter={[24, 24]}><Col xs={6} className="center"><Space><TreeComponent setSelectedPlayers={setSelectedPlayers} /></Space></Col><Col xs={6}><p className="content-title">详细信息</p><ContentComponent selectedPlayers={selectedPlayers} /></Col><Col xs={6}><p className="content-title">视频播放</p><VideoComponent /></Col></Row></div></Card></div>)
}export default Players
http://www.dtcms.com/a/601771.html

相关文章:

  • C++四种类型转换cast,其在参数传递时的作用
  • 什么网站可以做图赚钱网站建设主体设计要求
  • 云手机的核心价值
  • L10_参数验证
  • 免费网站制作手机软件的appaso排名优化
  • Java是编译型语言吗?解析Java语言的编译与执行过程
  • Hugging face微调 GPT-2模型
  • 1.3 半监督学习黑科技:如何用少量标注数据提升模型性能,节省90%标注成本?
  • 声学超材料与AI驱动的声振仿真优化设计
  • 罗湖建设公司网站建设企业推广app
  • 2025最新版Python 3.14.0安装使用指南
  • Keil5创建新工程时找不到STM32芯片
  • 重庆企业免费建站网站开发前台后台怎么交互
  • html5可以做交互网站吗西安模板建站公司
  • PostgreSQL 可视化监控利器 —— pg_top 安装与使用全攻略(查看正在执行的SQL占用的资源)
  • C语言 | 文件操作详解与实战示例
  • Spring ThreadPoolTaskExecutor 与 CompletableFuture.supplyAsync 常用实践
  • 网站太卡怎么优化wordpress meta key
  • 现在入局自助KTV,算晚吗?
  • 用Microsoft Visual Studio Installer Projects 2022打包程序,同时安装VC++的运行库等
  • 南宁网站制作建设建设网站需要哪些语言
  • C语言在线编译工具 | 提升编程效率与学习体验
  • ARM《10》_03_字符设备驱动进阶(并发→竞态→同步机制、阻塞io和非阻塞io)
  • 【DaisyUI]】dropdown在点击菜单项之后不关闭,怎么破?
  • 常德公司做网站手机网站怎么做的好
  • 网站建设的可行性报告范文城市宣传片制作公司
  • Go语言编译型:高效的编程语言选择|深入探讨Go语言的编译特性与优势
  • 邓州网站制作企业网站设计思路
  • 区县政府税务数据分析能力建设DID(2007-2025)
  • Python图像处理基础(十九)