《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.pswp.cn/news/897562.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/897562.shtml
英文地址,請注明出處:http://en.pswp.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.…