React+Ant design

文章目录

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

React

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

javascript 复制代码
return ({
	<>
		<span></span>
		<span></span>
	</>
})

1. 样式 className

样式书写

  1. className
  2. style={``{width:'100vh',height:'200px'}}
  3. 样式定义为变量【建议】
javascript 复制代码
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
javascript 复制代码
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.返回一个函数,先执行返回函数,再执行参数函数
javascript 复制代码
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可以传递一个创建函数和依赖项,创建函数会需要返回一个值,只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值。简单来说,作用是让组件中的函数跟随状态更新(即优化函数组件中的功能函数)。

javascript 复制代码
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
js 复制代码
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保存的对象发生改变,不会主动通知,属性变更不会重新渲染
js 复制代码
function InputFocus() {
  // 创建一个"抽屉"存输入框DOM
  const inputRef = useRef(null);

  return (
    <div>
      {/* 把输入框"放进抽屉" */}
      <input ref={inputRef} />
      {/* 点击按钮时,从"抽屉"里拿出来并聚焦 */}
      <button onClick={() => inputRef.current.focus()}>聚焦输入框</button>
    </div>
  );
}
6. useContext------组件的 "共享公告板"
  • 让组件之间 "共享数据",避免一层层传递(比如爷爷组件给孙子组件传数据,不用经过爸爸组件)。
javascript 复制代码
// 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从而更新状态
javascript 复制代码
// 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. 判断语句

  • 三元表达式
javascript 复制代码
<div>{selectType === 'player' ? '参赛者信息' : '设备信息'}</div>
javascript 复制代码
<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>
  • 使用判断语句
javascript 复制代码
{panelStatus !== 'off' && (
  <Button
    className="toggle-btn"
    type="primary"
    size="small"
    onClick={() => setInfoCollapsed(!infoCollapsed)}
    icon={
      infoCollapsed ? <DoubleLeftOutlined /> : <DoubleRightOutlined />
    }
  />
)}
  • 使用 CSS 控制显示隐藏
javascript 复制代码
<Button
  className={`toggle-btn ${panelStatus === 'off' ? 'hidden' : ''}`}
  type="primary"
  size="small"
  onClick={() => setInfoCollapsed(!infoCollapsed)}
  icon={
    infoCollapsed ? <DoubleLeftOutlined /> : <DoubleRightOutlined />
  }
/>

.hidden {
  display: none;
}
  • 使用内联样式
javascript 复制代码
<Button
  className="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')}>

javascript 复制代码
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. 父组件
js 复制代码
<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. 子组件
javascript 复制代码
<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>
      
      {/* 子组件输入框,数据会传递给父组件 */}
      <input
        type="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 树

javascript 复制代码
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 } = Input

const 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.target
     if (!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>
      <Search
        style={{ margin: '10px 0', width: 250 }}
        placeholder="搜索"
        onChange={onChange}
      />
      <Tree
        className="players-tree"
        treeData={treeData}
        checkable
        onCheck={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>
                  <Tooltip
                    placement="right"
                    title={playersInfo['label'] || '-'}
                  >
                    <span
                      style={{
                        whiteSpace: 'nowrap',
                        overflow: 'hidden',
                        textOverflow: 'ellipsis',
                        display: 'inline-block',
                        maxWidth: '90px',
                      }}
                    >
                      {playersInfo['label'] || '-'}
                    </span>
                  </Tooltip>
                </p>
                <p className="mb1rem">
                  <span>性别: </span>
                  <span
                    style={{
                      color:
                        playersInfo['性别'] === '男'
                          ? '#0eaff3'
                          : playersInfo['性别'] === '女'
                            ? '#ed08ac'
                            : 'inherit',
                    }}
                  >
                    {playersInfo['性别'] || '-'}
                  </span>
                </p>
                <p className="mb1rem" style={{ display: 'flex' }}>
                  <span>国籍: </span>
                  {playersInfo['国籍'] && playersInfo['国籍'] !== '-' ? (
                    <span
                      style={{
                        display: 'flex',
                        alignItems: 'center',
                        gap: '4px',
                      }}
                    >
                      <img
                        src={`/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>
      <Modal
        title="八百流沙极限赛视频"
        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> */}
            <iframe
              src="//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
相关推荐
一只小阿乐6 小时前
react 封装弹框组件 传递数据
前端·javascript·react.js
533_7 小时前
[element-plus] el-tree 动态增加节点,删除节点
前端·javascript·vue.js
禁止摆烂_才浅7 小时前
前端开发小技巧-【JavaScript】- 获取元素距离 document 顶部的距离
前端·javascript·react.js
wshzd7 小时前
LLM之Agent(二十九)|LangChain 1.0核心组件介绍
前端·javascript·langchain
程序猿_极客7 小时前
Vue 2脚手架从入门到实战核心知识点全解析(day6):从工程结构到高级通信(附代码讲解)
前端·javascript·vue.js·vue2学习笔记
一只小阿乐7 小时前
vue3 使用v-model开发弹窗组件
javascript·vue.js·elementui
web加加7 小时前
vue3 +vite项目页面防f12,防打开控制台
前端·javascript·vue.js
遥遥晚风点点9 小时前
Spark导出数据文件到HDFS
前端·javascript·ajax
克里斯蒂亚诺更新9 小时前
微信小程序 点击某个marker改变其大小
开发语言·前端·javascript