react受控模式和非受控模式(日历的实现)

前言

在现代Web开发中,React凭借其组件化开发和声明式编程的特性,已成为构建交互式用户界面的首选框架。本文将带您系统了解React项目开发的完整流程,从项目初始化到复杂组件实现,涵盖以下核心内容:

  1. 项目搭建与环境配置

    通过Vite快速创建React项目,解析dependenciesdevDependencies的本质区别,掌握生产环境与开发环境的依赖管理策略。

  2. 表单组件开发范式

    深度解析受控组件与非受控组件的实现差异,通过代码示例演示状态驱动(useState+onChange)与默认值控制(defaultValue)两种开发模式的应用场景。

  3. 复杂组件实战:日历控件

    从零实现一个完整的月视图日历组件,涵盖以下技术要点:

    • 日期计算逻辑(月份切换、日期填充)
    • 组件状态管理与外部通信(defaultValue初始化 + onChange回调)
    • CSS Flex布局实现网格日历视图
    • 交互优化(选中态反馈、悬停效果)

日期api

1. 创建日期对象

方法 说明
new Date() 创建当前日期和时间
new Date(timestamp) 通过时间戳创建(毫秒数,1970-01-01起)
new Date("2024-07-07") 通过日期字符串创建(ISO格式推荐)
new Date(2024, 6, 7, 12, 30) 通过年、月、日、时、分创建(月从0开始

2. 获取日期组件

方法 返回值范围 说明
.getFullYear() 4位数年份 2024
.getMonth() 0-11 0=1月, 11=12月
.getDate() 1-31 月中的第几天
.getDay() 0-6 0=周日, 1=周一
.getHours() 0-23 小时
.getMinutes() 0-59 分钟
.getSeconds() 0-59
.getMilliseconds() 0-999 毫秒
.getTime() 时间戳 1970年至今的毫秒数

UTC 版本

.getUTCHours().getUTCMonth() 等,用法与本地时间相同。


3. 设置日期组件

方法 说明
.setFullYear(year) 设置年份
.setMonth(month) 设置月份(0-11)
.setDate(day) 设置月中日期
.setHours(hours) 设置小时(可链式设置)
.setTime(timestamp) 通过时间戳设置

注意 :设置方法会直接修改原 Date 对象。


4. 日期格式化

方法 示例输出 说明
.toString() "Sun Jul 07 2024 12:30:00 GMT+0800" 完整日期字符串
.toDateString() "Sun Jul 07 2024" 仅日期部分
.toTimeString() "12:30:00 GMT+0800" 仅时间部分
.toISOString() "2024-07-07T04:30:00.000Z" ISO标准格式
.toLocaleString() "2024/7/7 12:30:00" 本地化格式
.toLocaleDateString() "2024/7/7" 本地化日期
.toLocaleTimeString() "12:30:00" 本地化时间

5. 其他实用方法

方法 说明
Date.now() 返回当前时间戳(毫秒)
Date.parse("2024-07-07") 解析字符串返回时间戳
.valueOf() 等价于 .getTime()

401未经授权

创建项目

npx create-react-app xxxx

使用vite来创建项目

使用前需要安装vite

使用npm install -g vite

第一种方式npm create vite@latest my-app -- --template react 第二种方式npm create-vite

什么是依赖?依赖的种类?

dependencies

  • 生产依赖:项目开发完成后,denpendencies中的第三方源代码也会被打包进来

devDependencies

  • 开发依赖:只在项目开发过程中有意义,devDependencies中的第三方源代码不会被打包进最终的项目中

在创建项目的终端执行npm i

自动识别我们需要的依赖并且帮我们安装

终端使用npm run dev运行项目

antd寻找我们需要的组件样式

什么是受控模式和非受控模式?

受控模式 vs 非受控模式

  • 表单中的 input
  1. 用户修改 input 值
  2. 代码修改 input 值
  • 能用代码设置表单的初始值,但是无法再次修改 value,能修改value的只有用户,这种就叫做 非受控模式

受控模式

  • 通常情况下,不建议使用受控模式 每次都会重新渲染组件,造成过多性能消耗

  • 当需要对输入的值进行特殊处理,处理完在设置到表单或则要实时处理,把状态同步到组件的时候用受控组件

js 复制代码
import { useState } from "react";

function App() {
  const [value, setValue] = useState("hello");

  console.log('App');
  

  function onChange(e) {
    // setValue(e.target.value);
    setValue(e.target.value.toUpperCase());

    console.log(e.target.value);
  }

  return <input type="text" value={value} onChange={onChange} />;
}
//声明死了value,需要通过代码setvalue()更改值
export default App;
//受控模式
js 复制代码
import { useEffect, useState } from "react";
async function queryData() {
  const data = await new Promise((resolve) => {
    setTimeout(()=>{
      resolve(666)
    },2000)
  });
  return data;
}
function App() {
const [num,setNum]=useState(0)
useEffect(()=>{
  queryData().then(res=>{
    setNum(res)
  })
},[])
  return(
    <div>{num}</div>
  )
}

export default App;
//受控模式

非受控模式

表单defaultValue默认属性值设为hello word

js 复制代码
function App() {

function  onChange(e) {
  console.log(e.target.value);//事件对象 表单对象身上的值
  
}
  
  return <input type="text"  defaultValue={"hello world"}  onChange={onChange}/>
}
export default App;

组件实战开发

日历的实现

核心功能
  1. 日历组件:显示月视图日历,支持日期选择

  2. 月份导航:通过左右箭头按钮切换上/下个月

  3. 日期选择:点击日期可选中并触发回调

  4. 状态管理

    • 使用 useState 管理当前显示的日期
    • 通过 props 接收默认值 (defaultValue)
    • 日期变化时通过 onChange 回调通知父组件
js 复制代码
import { useState } from "react";
import "./index.css";
function Calendar(props) {
const {defaultValue,onChange}=props
// 对象的解构
const [date, setDate]=useState(defaultValue);
const handlePrevMonth=()=>{ 
  setDate(new Date(date.getFullYear(),date.getMonth()-1,1))
}
const handleNextMonth=()=>{ 
  setDate(new Date(date.getFullYear(),date.getMonth()+1,1))
}

const daysOfMonth=(year,month)=>{
return new Date(year,month+ 1,0).getDate();
}

const firstDayOfMonth=(year,month)=>{
return new Date(year,month,1).getDay();
}
// getDate()方法参数6,0 日期是从1号开始的,如果写0会自动变成一个月的最后一天
// getDay()得到的是星期几

const renderDates=()=>{
const days=[]
const daysCount=daysOfMonth(date.getFullYear(),date.getMonth())
const firstDay=firstDayOfMonth(date.getFullYear(),date.getMonth())

for(let i=0;i<firstDay;i++){
  days.push(<div key={`empty--${i}`} className="empty"></div>)
}

for(let i=1;i<=daysCount;i++){ 
const clickHandler=()=>{ 
  const curDate=new Date(date.getFullYear(),date.getMonth(),i)
  setDate(curDate)
  // 对每个日期添加点击函数
  onChange(curDate)
}
  if(i===date.getDate()){ 
days.push(<div key={i} className="day selected" onClick={()=>{clickHandler()}}>{i}</div>)
  }

  else{days.push(<div key={i} className="day" onClick={()=>{clickHandler()}}>{i}</div>)}

}

return days

}

return (  
  <div className="calendar">
    <div className="header">
      <button  onClick={handlePrevMonth}>&lt;</button>
      <div>{date.getFullYear()}年{date.getMonth()+1}月</div>
        <button onClick={handleNextMonth}>&gt;</button>
    </div>
    <div className="days">
      <div className="day">日</div>
        <div className="day">一</div>
        <div className="day">二</div>
        <div className="day">三</div>
        <div className="day">四</div>
        <div className="day">五</div>
        <div className="day">六</div>
        {renderDates()}
    </div>
  </div>
)
}
export default Calendar;
  1. 月份处理

    • JavaScript 的 Date 对象中月份从 0 开始(0=1月)
    • 显示时需 month + 1,操作时需注意原始值
  2. 日期选择逻辑

    • 点击日期时创建新的日期对象:new Date(year, month, day)
    • 同时更新组件状态和触发父组件回调
js 复制代码
.calendar {

  width: 300px;

  height: 250px;

  border: 1px solid black;

  padding: 10px;

}

  


.header {

  display: flex;

  justify-content: space-between;

  align-items: center;

  height: 40px;

}

  


.days {

  display: flex;

  flex-wrap: wrap;

}

  


.empty,.day {

  width: calc(100% / 7);

  height: 30px;

  text-align: center;

}

  


.day:hover,.selected{

  background-color: #ccc;

  cursor: pointer;

}
js 复制代码
import Calendar from "./calendar";
//通过defaultValue来传入初始值date
// 修改date之后可以在onChange里拿到最新的值  ---非受控
function App(){
 return(
<Calendar defaultValue={new Date(2025,6,8)}  onChange={(newDate) => { alert(newDate.toLocaleDateString())
//父传子
}} />

)
}
export default App

快来试试写一个日历吧

相关推荐
Qrun42 分钟前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp43 分钟前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.2 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl4 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫5 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友5 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理7 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻7 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
mapbar_front8 小时前
在职场生存中如何做个不好惹的人
前端
牧杉-惊蛰8 小时前
纯flex布局来写瀑布流
前端·javascript·css