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

快来试试写一个日历吧

相关推荐
一tiao咸鱼2 分钟前
如何简单使用 prompt
前端·aigc
cdbqss17 分钟前
VB.net编写的身份证类
前端·.net
骑自行车的码农25 分钟前
React短文系列 遍历fiber树 App的创建
前端·react.js
AskSky28 分钟前
为了搞一个完美的健身APP,我真是费尽心机
前端
斯~内克34 分钟前
基于Vue.js和PDF-Lib的条形码生成与批量打印方案
前端·vue.js·pdf
阴阳怪气乌托邦35 分钟前
别再啃OA代码了!低代码"搭积木"式搞数智化,我直接少写500行
前端·低代码
beelan39 分钟前
v-on的思考
前端
山河木马42 分钟前
前端学习C++之:.h(.hpp)与.cpp文件
前端·javascript·c++
用户92724725021942 分钟前
PHP + CSS + JS + JSON 数据采集与展示系统,支持伪静态
前端
努力只为躺平1 小时前
一文搞懂 Promise 并发控制:批量执行 vs 最大并发数,实用场景全解析!
前端·javascript