《React 属性与状态江湖:从验证到表单受控的实战探险》

属性初识

属性能解决两个大问题:通信和复用

props.js:
import React, { Component } from 'react'
import Navbar from './Navbar'export default class App extends Component {state = {a:100}render() {return (<div><div><h2>首页</h2><Navbar title="首页" leftshow={false}/></div><div><h2>列表</h2><Navbar title="列表" leftshow={true}/></div><div><h2>购物车</h2><Navbar title="购物车" leftshow={true}/></div></div>)}
}
index.js:
import React, { Component } from 'react'export default class Navbar extends Component {state = {//只能内部自己用的,外面无法改变}// 属性是父组件传来的,this.propsrender() {let { title,leftshow } = this.propsconsole.log(leftshow)return (<div>{leftshow && <button>返回</button>}Navbar-{title}<button>home</button></div>)}
}

属性验证

添加属性验证:

import React, { Component } from 'react'
import PropTypes from 'prop-types'export default class Navbar extends Component {state = {//只能内部自己用的,外面无法改变}// 属性是父组件传来的,this.propsrender() {let { title,leftshow } = this.propsconsole.log(leftshow)return (<div>{leftshow && <button>返回</button>}Navbar-{title}<button>home</button></div>)}
}//类属性
Navbar.propTypes = {title:PropTypes.string,leftshow:PropTypes.bool
}

如果是在外面证明是类属性,在里面是对象属性

import React, { Component } from 'react'
import PropTypes from 'prop-types'export default class Navbar extends Component {state = {//只能内部自己用的,外面无法改变}static propTypes = {title:PropTypes.string,leftshow:PropTypes.bool
}// 属性是父组件传来的,this.propsrender() {let { title,leftshow } = this.propsconsole.log(leftshow)return (<div>{leftshow && <button>返回</button>}Navbar-{title}<button>home</button></div>)}
}

这也是一种写法

默认属性

怎么加上默认属性呢?

import React, { Component } from 'react'
import PropTypes from 'prop-types'export default class Navbar extends Component {state = {//只能内部自己用的,外面无法改变}static propTypes = {title:PropTypes.string,leftshow:PropTypes.bool
}// 属性是父组件传来的,this.propsrender() {let { title,leftshow } = this.propsconsole.log(leftshow)return (<div>{leftshow && <button>返回</button>}Navbar-{title}<button>home</button></div>)}
}// 对属性加上默认值
Navbar.defaultProps = {leftshow:true
}

也可以像上面一样进行改写

属性注意

import React, { Component } from 'react'
import Navbar from './Navbar'export default class App extends Component {state = {a:100}render() {//从上面的父组件传来的一个对象var obj = {title:"测试",leftshow: false}return (<div><div><h2>首页</h2><Navbar title="首页" leftshow={false}/></div><div><h2>列表</h2><Navbar title="列表" leftshow={true}/></div><div><h2>购物车</h2><Navbar title="购物车" leftshow={true}/></div><Navbar title={obj.title} leftshow={obj.leftshow}/><Navbar {...obj}/></div>)}
}

当接收的和传过来的一致的时候,就可以简写了

可以这样来控制组件的显示:

props.js:

import React, { Component } from 'react'
import Navbar from './Navbar'
import Sidebar from './Sidebar'export default class App extends Component {render() {return (<div><Navbar title="导航"></Navbar><Sidebar bg="yellow" position="left"></Sidebar></div>)}
}

index.js:

import React from 'react'export default function Sidebar(props) {let {bg,position} = propsvar obj1 = {left:0}var obj2 = {right:0}var obj = {background:bg,width:"200px",position:"fixed"}var styleobj = position==="left"?{...obj,...obj1}:{...obj,...obj2}return (<div style={styleobj}><ul><li>11111</li><li>11111</li><li>11111</li><li>11111</li><li>11111</li><li>11111</li><li>11111</li><li>11111</li><li>11111</li></ul></div>)
}

状态VS属性

相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)

不同点:

1. 属性能从父组件获取,状态不能

2. 属性可以由父组件修改,状态不能

3. 属性能在内部设置默认值,状态也可以,设置方式不一样

4. 属性不在组件内部修改,状态要在组件内部修改

5. 属性能设置子组件初始值,状态不可以

6. 属性可以修改子组件的值,状态不可以 state 的主要作用是用于组件保存、控制、修改自己的可变状态。

state 在组件内部初始化,可以被 组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制 的数据源。 state 中状态可以通过 this.setState 方法进行更新, setState 会导致组件的重新渲染。

props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参 数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props ,否则组件的 props 永远保持 不变。

没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件 (stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有 状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。

孩子无法直接修改属性

import React, { Component } from 'react'class Child extends Component{render(){return <div>child-{this.props.text}<button>click-child</button></div>}
}export default class App extends Component {state = {text:"111111111"}render() {return (<div><button onClick={()=>{this.setState({text:'222222222'})}}>click</button><Child text={this.state.text}/></div>)}
}

表单的受控与非受控

受控组件非受控组件在狭义上是看是否调用ref

但是在广义上是React组件的数据渲染是否被调用者传递的props完全控制,控制则是受控组件,否则不是受控组件

非受控

如果React要编写一个非受控组件,那么久可以使用ref来从DOM节点中获取表单数据,就是非受控组件,比如这样:

import React, { Component } from 'react'export default class App extends Component {myusername = React.createRef()render() {return (<div><h1>登录页</h1><input type='text' ref={this.myusername}value="kerwin"/><button onClick={()=>{console.log(this.myusername.current )}}>登录</button><button onClick={()=>{this.myusername.current.value = ""}}>重置</button></div>)}
}

这种输入框是输不进去东西的,但是改成defaultvalue就不一样了

import React, { Component } from 'react'export default class App extends Component {myusername = React.createRef()render() {return (<div><h1>登录页</h1><input type='text' ref={this.myusername}defaultValue="kerwin"/><button onClick={()=>{console.log(this.myusername.current )}}>登录</button><button onClick={()=>{this.myusername.current.value = ""}}>重置</button></div>)}
}

这就是它在非受控组件中的应用

在原生JS中,onInput是监听输入的变化,onChange是输入变化且焦点移开才监听一次

但是它在React中和onInput一样了

受控

看看受控组件:

import React, { Component } from 'react';// 假设这里定义了 Child 组件
class Child extends Component {render() {return (<div>接收到的值: {this.props.myvalue}</div>);}
}export default class App extends Component {state = {username: "kerwin"};render() {return (<div><h1>登录页</h1><inputtype='text'value={this.state.username}onChange={(evt) => {console.log("onChange", evt.target.value);this.setState({username: evt.target.value});}}/><buttononClick={() => {console.log(this.state.username);}}>登录</button><buttononClick={() => {this.setState({username: ''});}}>重置</button>{/* 直接传递 state 中的 username */}<Child myvalue={this.state.username} /></div>);}
}

由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value ,这使得 React 的 state 成为 唯一数据源。

由于 handlechange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而 更新。

对于受控组件来说,输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI 元素,或者通过其他 事件处理函数重置,但这意味着你需要编写更多的代码

受控影院查询案例

对之前的影院案例做更改

import React, { Component } from 'react';
import axios from 'axios';
import BetterScroll from 'better-scroll';export default class Cinema extends Component {constructor(props) {super(props);this.state = {cinemaList: [],mytext:""};this.wrapperRef = React.createRef();this.bs = null;}componentDidMount() {axios({url: 'https://m.maizuo.com/gateway?cityId=610100&ticketFlag=1&k=1315991',headers: {'x-client-info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"1741595630655347584860161","bc":"610100"}','x-host': 'mall.film-ticket.cinema.list'}}).then((res) => {console.log(res.data);this.setState({cinemaList: res.data.data.cinemas,backcinemaList: res.data.data.cinemas}, () => {if (this.wrapperRef.current) {this.bs = new BetterScroll(this.wrapperRef.current);}});}).catch((error) => {console.error('请求出错:', error);});}render() {const { cinemaList } = this.state;return (<div><input value={this.state.mytext}onChange={(evt)=>{this.setState({mytext:evt.target.value})}}/><divref={this.wrapperRef}className="wrapper"style={{ height: '500px', background: 'yellow', overflow: 'hidden', position: 'relative' }}><div className='content' style={{ padding: '10px' }}>{this.getCinemaList().map((item) => (<dl key={item.cinemaId}><dt>{item.name}</dt><dd>{item.address}</dd></dl>))}</div></div></div>);}getCinemaList(){return this.state.cinemaList.filter(item =>item.name.toUpperCase().includes(this.state.mytext.toUpperCase()) ||item.address.toUpperCase().includes(this.state.mytext.toUpperCase()));}
}

获取数据之后进行betterScroll初始化,在修改完状态后,setState后传回调函数,再重新初始化betterScroll解决数据更新后betterScroll长度过长的问题

案例受控todolist

更改成受控组件的写法:

import React, { Component } from 'react'export default class App extends Component {state = {mytext:'',list: [{id: 1,mytext: 'aaa',},{id: 2,mytext: 'bbb',},{id: 3,mytext: 'ccc',},],}render() {return (<div><input value={this.state.mytext} onChange={(evt)=>{this.setState({mytext:evt.target.value})}}/><buttononClick={() => {this.handleClick()}}>add2</button><ul>{this.state.list.map((item,index) => (<li key={item.id}><span dangerouslySetInnerHTML={{__html:item.mytext}}></span><button onClick={()=>{this.handleDelClick(index)}}>删除</button></li>))}</ul><div className={this.state.list.length === 0 ?'':'hidden'}>暂无待办事项</div></div>)}handleClick = () => {let newlist = [...this.state.list]newlist.push({id:Math.random()*100000,        //生成不同id的函数mytext:this.state.mytext})this.setState({list: newlist,mytext:''})}handleDelClick(index){console.log(index)//不要直接修改状态,可能会造成不可预期的问题let newlist = this.state.list.concat()newlist.splice(index,1)this.setState({list:newlist})}
}

我们现在还想要添加一个效果:删除的时候不直接删除 ,而是添加删除线

添加状态,再添加删除线效果:

import React, { Component } from 'react'export default class App extends Component {state = {mytext:'',list: [{id: 1,mytext: 'aaa',isChecked:false},{id: 2,mytext: 'bbb',isChecked:false},{id: 3,mytext: 'ccc',isChecked:true},],}render() {return (<div><input value={this.state.mytext} onChange={(evt)=>{this.setState({mytext:evt.target.value})}}/><buttononClick={() => {this.handleClick()}}>add2</button><ul>{this.state.list.map((item,index) => (<li key={item.id}><input type='checkbox' checked={item.isChecked} onChange={()=>this.handleChecked(index)}/>{item.isChecked?'删除':'不删除'}<span dangerouslySetInnerHTML={{__html:item.mytext}}style={{textDecoration:item.isChecked?"line-through":""}}></span><button>完成</button><button onClick={()=>{this.handleDelClick(index)}}>删除</button></li>))}</ul><div className={this.state.list.length === 0 ?'':'hidden'}>暂无待办事项</div></div>)}handleChecked = (index)=>{let newlist = [...this.state.list]newlist[index].isChecked = !newlist[index].isCheckedthis.setState({list:newlist})    }handleClick = () => {let newlist = [...this.state.list]newlist.push({id:Math.random()*100000,        //生成不同id的函数mytext:this.state.mytext,isChecked:false})this.setState({list: newlist,mytext:''})}handleDelClick(index){console.log(index)//不要直接修改状态,可能会造成不可预期的问题let newlist = this.state.list.concat()newlist.splice(index,1)this.setState({list:newlist})}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897562.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qwen/QwQ-32B 基础模型上构建agent实现ppt自动生成

关心Qwen/QwQ-32B 性能测试结果可以参考下 https://zhuanlan.zhihu.com/p/28600079208https://zhuanlan.zhihu.com/p/28600079208 官方宣传上是该模型性能比肩满血版 DeepSeek-R1&#xff08;671B&#xff09;&#xff01; 我们实现一个 使用Qwen/QwQ-32B 自动生成 PowerPoi…

Javascript基础语法详解

面向对象的语言.脚本语言,不需要编译,浏览器解释即可运行 .用于控制网页的行为.浏览器的source可以打断点调试, console输入代码可以执行 use strict指令: 在“严格模式”下运行js代码, 防止意外创建全局变量等, 提高代码安全性和执行效率. 使用: 全局严格模式&#xff1a;…

[杂学笔记] TCP和UDP的区别,对http接口解释 , Cookie和Session的区别 ,http和https的区别 , 智能指针 ,断点续传

文章目录 1. TCP和UDP的区别2. 对http接口解释3. Cookie和Session的区别4. http和https的区别5. 智能指针6.断点续传 1. TCP和UDP的区别 tcp的特点&#xff1a; 面向连接&#xff0c;可靠性高&#xff0c;全双工&#xff0c;面向字节流udp特点&#xff1a;无连接&#xff0c;不…

JAVASE(五)

目录 一、成员变量和局部变量 1.定义 2.区别 &#xff08;1&#xff09;相同 &#xff08;2&#xff09;不同 二、方法和构造方法 1.定义 2.构造方法细节 3.方法重载 一、成员变量和局部变量 1.定义 &#xff08;1&#xff09;成员变量是…

Matlab中快速查找元素索引号

1、背景介绍 在算法设计过程中&#xff0c;有时候需要从一维/二维数组中&#xff0c;快速查找是否某个元素&#xff0c;以及该元素所在的位置。如一维矩阵[1 2 3 4 5 6 6 7 8]所示&#xff0c;元素6所在的位置为6 7。 2、函数测试 matlab中函数find()可以快速查找到指定元素所…

【DuodooTEKr 】多度科技 以开源之力,驱动企业数字化转型

多度科技 背景 / Background 在全球产业链重构与国内经济双循环的浪潮下&#xff0c;中国制造业与贸易企业正面临数字化升级的迫切需求。开源技术作为数字化转型的基石&#xff0c;不仅能打破技术壁垒、降低企业成本&#xff0c;更能通过协作创新加速产业智能化进程。 多度科技…

【HarmonyOS Next】鸿蒙应用故障处理思路详解

【HarmonyOS Next】鸿蒙应用崩溃处理思路详解 一、崩溃问题发现后定位 1. 崩溃现象&#xff1a; 常见的崩溃问题表现为&#xff0c;应用操作后白屏闪退&#xff0c;或者应用显示无响应卡死。 2.定位问题&#xff1a; 发现崩溃后&#xff0c;我们首先需要了解复现步骤&#x…

linunx ubuntu24.04.02装libfuse2导致无法开机进不了桌面解决办法

osu.appimage运行需要libfuse2 然后我就下了fuse,打了两把第二天无法开机 这样是不能开机的 这样是可以开机的 解决办法一&#xff1a;玩星火商店的osu&#xff0c;好了问题解决 解决办法二&#xff1a; 在这个页面 ctrl alt f2进入tty6 sudo apt install ubuntu-desktop 进…

Maven 的常用指令

一、核心构建指令 mvn clean 作用&#xff1a;删除 target 目录&#xff08;清理编译/打包生成的文件&#xff09;。 场景&#xff1a;确保从头开始构建&#xff0c;避免残留文件干扰。 mvn compile 作用&#xff1a;编译项目源代码。 场景&#xff1a;快速检查代码是否能编…

llvm数据流分析

llvm数据流分析 1.数据流分析2.LLVM实现2.1.常量传播2.2.活跃性分析 相关参考文档&#xff1a;DataFlowAnalysisIntro、ustc编译原理课程、南大程序分析课程1、南大程序分析课程2。 1.数据流分析 数据流分析在编译优化等程序分析任务上都有重要应用。通常数据流分析可被抽象为…

C++ MySQL 常用接口(基于 MySQL Connector/C++)

C MySQL 常用接口&#xff08;基于 MySQL Connector/C&#xff09; 1. 数据库连接 接口&#xff1a; sql::mysql::MySQL_Driver *driver; sql::Connection *con;作用&#xff1a; 用于创建 MySQL 连接对象。 示例&#xff1a; driver sql::mysql::get_mysql_driver_insta…

C++蓝桥杯基础篇(十一)

片头 嗨~小伙伴们&#xff0c;大家好&#xff01;今天我们来学习C蓝桥杯基础篇&#xff08;十一&#xff09;&#xff0c;学习类&#xff0c;结构体&#xff0c;指针相关知识&#xff0c;准备好了吗&#xff1f;咱们开始咯~ 一、类与结构体 类的定义&#xff1a;在C中&#x…

css中实现border距离视图左右两侧有距离

首先看效果图 再看css是如何实现 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>.main {background-color: aqua;display: block;width: 300px;padding: 0px 32px;box-sizing: border-box;}/…

Ubuntu 22.04 无法进入图形界面的解决方法

Ubuntu 22.04 无法进入图形界面&#xff0c;只能进入 tty&#xff0c;可能是由于图形界面相关的配置或驱动程序出现了问题。以下是一些常见的解决方法&#xff1a; 1. 检查图形界面服务状态 首先&#xff0c;检查图形界面服务&#xff08;通常是 gdm 或 lightdm&#xff09;的…

Tweak Power:全方位电脑系统优化的高效工具

在日常使用电脑时&#xff0c;系统性能的下降、垃圾文件的堆积以及硬盘的老化等问题常常困扰着用户。为了提升电脑性能、优化系统运行&#xff0c;许多人会选择系统优化工具。然而&#xff0c;国内一些系统优化软件常常因为广告过多或功能冗杂而让人望而却步。此时&#xff0c;…

深入浅出Bearer Token:解析工作原理及其在Vue、Uni-app与Java中的实现Demo

目录 前言1. 基本知识2. Demo3. 实战 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 1. 基本知识 Bearer Token是一种基于Token的认证机制&#xff0c;用于在HTTP请求中传递用户的身份信息 应用于RESTful A…

kubernetes——part3-5 核心概念 Service

一、 service作用 使用kubernetes集群运行工作负载时&#xff0c;由于Pod经常处于用后即焚状态&#xff0c;Pod经常被重新生成&#xff0c;因此Pod对应的IP地址也会经常变化&#xff0c;导致无法直接访问Pod提供的服务&#xff0c;Kubernetes中使用了Service来解决这一问题&am…

从零开始 | C语言基础刷题DAY1

❤个人主页&#xff1a;折枝寄北的博客 DAY1[2025.3.11] 1. 求两个数的较大值2.从键盘输入的两个数的大小关系3.一个整数的奇偶性&#xff0c;请判断4. 考试分数是否通过5.考试成绩是否完美&#xff0c;请判断 1. 求两个数的较大值 题目&#xff1a; 写一个函数求两个整数的较…

开源模型时代的 AI 开发革命:Dify 技术深度解析

开源模型时代的AI开发革命&#xff1a;Dify技术深度解析 引言&#xff1a;AI开发的开源新纪元 在生成式AI技术突飞猛进的2025年&#xff0c;开源模型正成为推动行业创新的核心力量。据统计&#xff0c;全球超过80%的AI开发者正在使用开源模型构建应用&#xff0c;这一趋势不仅…

Dify Web 前端独立部署指南(与后端分离,独立部署)

背景:单独拆分前端出来部署,二开前后端 本文档专注于 Dify Web 前端的部署流程和配置,适用于需要将项目部署到各种环境的运维人员和开发者。 1. 环境准备 1.1 部署环境要求 Node.js >= 18.17.0Nginx 或其他Web服务器(生产环境推荐)Docker(可选,用于容器化部署)1.…