前面寫了三千字沒保存,恨!
批量渲染
function App() {const list = [{id:0,text:'aaaa'},{id:1,text:'bbbb'},{id:2,text:'cccc'}]// for (let i = 0; i < list.length; i++) {// list[i] = <li>{list[i]}</li>// }return (<div><ul>{list.map((item) => <li key={item.id}>{item.text}</li>)}</ul></div>)
}
export default App
點標記寫法
// function Welcome() {
// return <div>hello Welcome</div>
// }// function App() {
// return (
// <div>
// <Welcome />
// <Welcome></Welcome>
// </div>
// )
// }
// export default Appconst Qf = () => {return <div>hello Qf</div>
}Qf.Welcome = () => {return <div>hello Welcome</div>
}function App() {return (<div><Qf.Welcome /><Qf.Welcome></Qf.Welcome></div>)
}
export default App
組件通信
import PropTypes from 'prop-types'
//子組件
function Welcome({ count, msg,isShow }) {return (<div>hello Welcome,{count},{msg},{isShow+''}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.string.isRequired, // 驗證 count 是字符串并且是必傳的msg: PropTypes.string.isRequired, // 驗證 msg 是字符串并且是必傳的isShow: PropTypes.string.isRequired, // 驗證isShow是字符串并且是必傳的
}//父組件
function App() {const count='我是個變量'return (<div><Welcome count={count} msg='hi react' isShow /></div>)
}
export default App
組件組合
import PropTypes from 'prop-types'
//子組件
function Welcome() {return <div>hello Welcome</div>
}function Head() {return <div>hello Head</div>
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.string.isRequired, // 驗證 count 是字符串并且是必傳的msg: PropTypes.string.isRequired, // 驗證 msg 是字符串并且是必傳的isShow: PropTypes.string.isRequired, // 驗證isShow是字符串并且是必傳的
}//父組件
function App() {return (<div>hello App<Welcome><Head /></Welcome></div>)
}
export default App
可以看見這個Head被包在Welcome里 是渲染不出來的
使用props的children屬性傳遞子組件
import PropTypes from 'prop-types'function Head() {return <div>hello Head</div>
}function Welcome({ children }) {return (<div>hello Welcome{children}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.string.isRequired, // 驗證 count 是字符串并且是必傳的children: PropTypes.string.isRequired,
}//父組件
function App() {return (<div>hello App<Welcome><Head /></Welcome></div>)
}
export default App
另一種寫法
import PropTypes from 'prop-types'function Head({ count }) {return <div>hello Head,{count}</div>
}function Welcome() {const count = 456return (<div>hello Welcome<Head count={count} /></div>)
}
// 為 Head 組件添加 prop-types 驗證
Head.propTypes = {count: PropTypes.number.isRequired, // 驗證 count 是數字并且是必傳的
}// 添加 prop-types 驗證
Welcome.propTypes = {
}//父組件
function App() {return (<div>hello App<Welcome><Head /></Welcome></div>)
}
export default App
import PropTypes from 'prop-types'// 為 Head 組件添加 prop-types 驗證
Head.propTypes = {count: PropTypes.number.isRequired, // 驗證 count 是數字并且是必傳的
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.number.isRequired, // 驗證 Welcome 組件的 count 是必傳的
}function Head({ count }) {//接收countreturn <div>hello Head,{count}</div>
}function Welcome() {const count = 456return (<div>hello Welcome<Head count={count} />{/* {掛載count} */}</div>)
}//父組件
function App() {const count = 123return (<div>hello App<Welcome count={count} /></div>)
}
export default App
如何分別傳遞多組內容
所有的內容放到children里,沒辦法獨立使用
import PropTypes from 'prop-types'function Welcome({ children }) {return (<div>{children}hello Welcome{children}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.string.isRequired, // 驗證 count 是字符串并且是必傳的children: PropTypes.string.isRequired,
}//父組件
function App() {return (<div>hello App<Welcome><div>aaaaaaaaaaaa</div><div>bbbbbbbbbbbb</div></Welcome></div>)
}
export default App
使用多組內容傳遞
import PropTypes from 'prop-types'function Welcome({ bottom, top }) {return (<div>{top}hello Welcome{bottom}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.string.isRequired, // 驗證 count 是字符串并且是必傳的children: PropTypes.string.isRequired,top: PropTypes.string.isRequired,bottom: PropTypes.string.isRequired,
}//父組件
function App() {return (<div>hello App<Welcometop={<div>aaaaaaaaaaaa</div>}bottom={<div>bbbbbbbbbbbb</div>}></Welcome></div>)
}
export default App
通信的數據如何添加默認值
import PropTypes from 'prop-types'function Welcome({ count, msg }) {return (<div>hello Welcome,{count},{msg}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.string.isRequired, // 驗證 count 是字符串并且是必傳的children: PropTypes.string.isRequired,msg: PropTypes.string.isRequired,
}//父組件
function App() {return (<div>hello App{/* { 傳遞參數的情況} */}<Welcome count={123} msg='hello react' />{/* { 不傳遞參數的情況} */}<Welcome /></div>)
}
export default App
如果有接收參數的位置,但是不傳遞參數,就會這樣👇
為了解決這個問題,就提供了默認參數
ES6是這么做的:
import PropTypes from 'prop-types'// function Head() {
// return <div>hello Head</div>
// }function Welcome({ count='我是count的默認值', msg='我是msg的默認值'}) {return (<div>hello Welcome,{count},{msg}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.string.isRequired, // 驗證 count 是字符串并且是必傳的children: PropTypes.string.isRequired,msg: PropTypes.string.isRequired,
}//父組件
function App() {return (<div>hello App{/* { 傳遞參數的情況} */}<Welcome count={123} msg='hello react' />{/* { 不傳遞參數的情況} */}<Welcome /></div>)
}
export default App
也可以使用React原生的:
import PropTypes from 'prop-types'function Welcome({ count,msg}) {return (<div>hello Welcome,{count},{msg}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.number, // 驗證 count 是數字(非必傳)msg: PropTypes.string, // 驗證 msg 是字符串(非必傳)
}Welcome.defaultProps = {count: 0,msg:'我是默認值'
}
//父組件
function App() {return (<div>hello App{/* { 傳遞參數的情況} */}<Welcome count={123} msg='hello react' />{/* { 不傳遞參數的情況} */}<Welcome /></div>)
}
export default App
但是我失敗了
為什么?因為:
我是19,別管了就用es6吧
通信數據如何限定類型
一般更推薦ts,但是ts不一定是項目用的
類型驗證就用我之前已經寫過的這部分👇
// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.number, // 驗證 count 是數字(非必傳)msg: PropTypes.string, // 驗證 msg 是字符串(非必傳)
}
瀏覽器莫名其妙不報錯,可能還是react的版本問題
如果希望有個數據傳過來,數字和字符串都是符合要求的類型,也就是篩出兩種類型可以這么寫:
// 添加 prop-types 驗證
Welcome.propTypes = {count:PropTypes.oneOfType([PropTypes.string,PropTypes.number,]),// 驗證 count 是數字或字符串(非必傳)msg: PropTypes.string, // 驗證 msg 是字符串(非必傳)
}
還可以有一些更復雜的限定,比如限定值是多少:
import PropTypes from 'prop-types'function Welcome({ count, msg }) {return (<div>hello Welcome,{count},{msg}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 驗證 count 是數字或字符串(非必傳)msg: PropTypes.string, // 驗證 msg 是字符串(非必傳)type: PropTypes.oneOf(['primary', 'success', 'error']),
}Welcome.defaultProps = {count: 0,msg: '我是默認值',
}
//父組件
function App() {return (<div>hello App{/* { 正確的情況} */}<Welcome count={123} msg='hello react' type='我是錯的' /></div>)
}
export default App
這里按理來說應該會報錯,但是并沒有!!!
傳值其實也可以傳jsx:
import PropTypes from 'prop-types'function Welcome({ count, msg }) {return (<div>hello Welcome,{count},{msg}</div>)
}// 添加 prop-types 驗證
Welcome.propTypes = {count: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 驗證 count 是數字或字符串(非必傳)msg: PropTypes.string, // 驗證 msg 是字符串(非必傳)type: PropTypes.oneOf(['primary', 'success', 'error']),icon: PropTypes.element//針對jsx的類型,element
}Welcome.defaultProps = {count: 0,msg: '我是默認值',
}
//父組件
function App() {return (<div>hello App{/* { 正確的情況} */}<Welcomecount={123}msg='hello react'type='primary'icon={<div className='icon-close'></div>}/></div>)
}
export default App
組件必須是個純函數
react的嚴格模式會檢測我們當前的組件是不是純函數
純函數組件是指:
組件的輸出(渲染結果)完全由輸入(props 和 state)決定。
組件沒有副作用(例如直接修改 DOM、發起網絡請求、使用?
setTimeout
?等)。組件在相同的 props 和 state 下,總是返回相同的渲染結果
兩次結果一樣
在嚴格模式下,React 會?故意調用兩次?函數組件(包括其渲染邏輯),如果組件是純函數,兩次調用的結果應該完全相同。
比如++操作,如果把對count的聲明寫函數外面,那么就不是一個純函數:
預期來說count=2,最后等于3是因為React故意調用兩次函數組件,兩次的結果一次為2,一次為3,所以不是純函數組件
這樣就是了
function App() {let count = 1count++console.log(count)return <div>{count}</div>
}
export default App
兩次結果相同
當然,不開嚴格模式的話就沒有這種情況啦,一般建議開啟
相同的輸入和輸出
意思就是給函數傳入相同的參數,就應該傳出相同的輸出
這是一個純函數,傳入2都是4
這不是純函數,傳入2得到的輸出不一定相同
純函數可以保證我們的組件更健壯
組件的狀態和useState
?useState基礎使用
useState 是一個 React Hook(函數),它允許我們向組件添加一個狀態變量(State), 從而控制影響組件的渲染結果
狀態變量是 React 組件內部的一個特殊變量,用于存儲組件的數據,當狀態變量的值發生變化時,React 會自動重新渲染組件,以反映最新的數據
狀態變量和普通JS變量不同的是,狀態變量一旦發生變化組件的視圖UI也會跟著變化(數據驅動視圖)
useState是React里的方法,返回的是個數組,數組里有兩項:[狀態變量,修改狀態變量的方法]
只有用這個專門的修改狀態變量的方法修改變量,才會在ui渲染里起效
//useState實現一個計數器按鈕
import { useState } from 'react'
function App() {// const handleClick = (name,e) => console.log('button被點擊了name:',name,'e:',e)//1、調用useState添加一個狀態變量const [count, setCount] = useState(0)//2、點擊事件回調const handleClick = () => { setCount(count+1) }return (<div className="App"><button onClick={handleClick}>{count}</button></div>);
}
export default App;
count++
?會直接修改?count
?的值,而 React 的狀態變量是不可變的(Immutable),不能直接修改。
那我就要問了:const handleClick = () => { setCount(count++) }這么寫不也是通過setState 函數更新狀態嗎?為什么不行?
count++
會先返回當前的值,再進行自增操作,而 React 的 setState
需要傳入新的值,而不是修改原值。
那++count呢?++count
會先對 count
進行自增操作,然后返回自增后的值。這個操作會直接修改當前 count
的值,而 React 需要的是通過 setCount
來觸發狀態的更新,這就相當于狀態變量的修改是你自己改的,不是通過對應的方法改的
正確的寫法是應該直接傳入 count + 1
,而不是 count++
,因為 count++
只是修改了本地變量
對于對象來說也是這樣,應該把新的對象寫進對應方法,而不是直接修改
import { useState } from 'react'function App () {let [count, setCount] = useState(0)const handleClick = () => {// 直接修改 無法引發視圖更新// count++// console.log(count)setCount(count + 1)}// 修改對象狀態const [form, setForm] = useState({ name: 'jack' })const changeForm = () => {// 錯誤寫法:直接修改// form.name = 'john'// 正確寫法:setFrom 傳入一個全新的對象setForm({...form,name: 'john'})}return (<div><button onClick={handleClick}>{count}</button><button onClick={changeForm}>修改form{form.name}</button></div>)
}export default App
狀態如何改變視圖
先看這段代碼
function App() {let count = 0const increment = () => {count++ // 修改普通變量console.log(count) // 值會變化,但不會觸發重新渲染}console.log(1234567)return (<div><p>{count}</p><button onClick={increment}>Increment</button></div>)
}
export default App
點擊按鈕時,count
?的值會增加,但頁面不會重新渲染,因此用戶看不到變化。
狀態變量會被記憶上一次的值,每次渲染函數返回的內容不一樣,所以在察覺變化的時候React會重新調用渲染函數,并且改變count,所以每次結果都不一樣)
普通變量的變化不會通知 React,所以React 不會調用渲染函數,所以不會重新渲染jsx。
順便:Mujica第八集拍出來其實是為了縮短中國人壽命降低生產力,是日本人為了打敗中國人投放的精神類武器,是日本成為東亞第一強國的邪惡計劃最核心的一步