- React元素的屬性名使用駝峰命名法
- 特殊屬性名:class -> className for -> htmlFor tabindex -> tabIndex
- 沒有子節(jié)點的React元素可以用/>結束
- 推薦:使用小括號包裹JSX说墨,從而避免JS中的自動插入分號陷阱
const dv = (
<div>hello</div>
)
- jsx中是使用Javascript表達式
- 數(shù)據(jù)儲存在JS中
- 語法:{JavaScript表達式} 注意不是雙大括號析恋!
const name = 'jack'
const dv = (
<div>你好塘揣,我叫{name}</div>
)
6.條件渲染
// 條件渲染
const isLoading = true
const loadData = () => {
if(isLoading){
return <div>loading...</div>
}
return <div>數(shù)據(jù)加載完成,此處顯示加載后的數(shù)據(jù)</div>
}
const title = (
<h1>條件渲染:{loadData()}</h1>
)
ReactDOM.render(title,document.getElementById('root'))
還可以使用if/else或三元運算符或邏輯與運算符來實現(xiàn)
//三元表達式
const loadData = () => {
return isLoading ? (<div>loading...</div>) : (<div>數(shù)據(jù)加載完成,此處顯示加載后的數(shù)據(jù)</div>)
}
//邏輯與運算符
const loadData = () => {
return isLoading && (<div>loading...</div>)
}
isLoading是false不會執(zhí)行后面的,true才會執(zhí)行
7.列表渲染
const songs = [
{name:'qwe',id:1},
{name:'dfddg',id:2},
{name:'hsd',id:3}
]
const title = (
<ul>
{songs.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
)
注意:
- 列表要渲染一組數(shù)據(jù),應該使用map()方法
- 列表渲染應該添加key屬性,key屬性的值要保證唯一
- 原則:map()遍歷誰静檬,就給誰添加key屬性
- 注意:盡量避免使用索引號作為key,索引號可變不穩(wěn)定
8.JSX樣式處理
- 行內(nèi)樣式--style
const list = (
<h1 style={{color:'red',backgroundColor:'skyblue'}}>
jsx樣式處理
</h1>
)
- className樣式
//導入
import './index.css'
const list = (
<h1 className="title" style={{color:'red',backgroundColor:'skyblue'}}>
jsx樣式處理
</h1>
)
//index.css
.title{
text-align: center;
}
9.react組件的兩種創(chuàng)建方式
- 9.1 使用函數(shù)創(chuàng)建組件
- 使用js函數(shù)或者箭頭函數(shù)創(chuàng)建
- 函數(shù)名首字母大寫并级,react以此區(qū)分 組件 和普通的 元素
- 函數(shù)要有return值
- 渲染函數(shù)組件:用函數(shù)名作為組件標簽名
- 組件標簽可以是單標簽也可以是雙標簽
function Hello(){
return(
<div>我的第一個react組件拂檩!</div>
)
}
ReactDOM.render(<Hello />,document.getElementById('root'))
箭頭函數(shù)
const Hello = () => <div>我的第一個react組件</div>
- 9.2 使用類創(chuàng)建組件
類組件:使用ES6的class創(chuàng)建的組件
約定1:類名也必須以大寫字母開頭
約定2:類名稱應該繼承React.Component父類,從而可以使用父類中提供的方法或?qū)傩?br> 約定3:類組件必須提供render()方法
約定4:render()方法必須有返回值死遭,表示該組件的結構
class Hello extends React.Component{
render(){
return(
<div>我的第一個react類組件广恢!</div>
)
}
}
9.3 抽離為獨立的js文件
組件作為獨立的個體,獨立存放一個組件
- 創(chuàng)建Hello.js文件
- 在文件中導入react
- 創(chuàng)建組件
- 在Hello.js中導出該組件
- 在index.js中導入該組件
- 渲染組件
10.點擊事件:駝峰命名法呀潭,
- 函數(shù)組件綁定事件
// 通過函數(shù)組件綁定事件
function App() {
function handleClick(){
console.log('函數(shù)組件中的事件綁定钉迷,事件觸發(fā)了')
}
return(
<button onClick={handleClick}>點我,點我</button>
)
}
ReactDOM.render(<App />,document.getElementById('root'))
- 類組件
class App extends React.Component{
handleClick(){
console.log('單擊事件觸發(fā)了')
}
render(){
return(
<button onClick={this.handleClick}>點我钠署,點我</button>
)
}
}
ReactDOM.render(<App />,document.getElementById('root'))
11.事件對象
- 通過時間處理程序獲取到事件對象
- react中的事件對象叫做:合成事件(對象)
- 合成事件:兼容所有瀏覽器糠聪,無需擔心跨瀏覽器兼容性問題
12.有狀態(tài)組件和無狀態(tài)組件
- 函數(shù)組件又叫做無狀態(tài)組件,類組件又叫做有狀態(tài)組件
- 狀態(tài)(state)即數(shù)據(jù)
- 函數(shù)組件沒有自己的狀態(tài)谐鼎,只負責數(shù)據(jù)展示(靜)
- 類組件有自己的狀態(tài)舰蟆,負責更新UI,讓頁面”動“起來
13.組件中的state和setState
- 狀態(tài)(state)即數(shù)據(jù)狸棍,是組件內(nèi)部私有數(shù)據(jù)
- state的值是對象身害,表示一個組件中可以有多個數(shù)據(jù)
- 狀態(tài)改變,語法:this.setState({要修改的數(shù)據(jù)})草戈,注意塌鸯,不要直接修改state中的值,這是錯誤L破1!
this.setState({
number: this.number + 1
})
- setState()作用:1.修改state 2.更新UI
- 思想:數(shù)據(jù)驅(qū)動視圖
14.抽離onclick的方法费韭,解決this指向問題
- 1.箭頭函數(shù)茧球,利用箭頭函數(shù)自身不綁定this的特點
class App extends React.Component{
state = {
count: 0,
}
onIncrement() {
this.setState({
count: this.state.count + 1
})
}
render(){
return(
<div>
<h1>計數(shù)器:{this.state.count}</h1>
//箭頭函數(shù)中的this指向外部環(huán)境,此處為:render()方法
<button onClick={()=>this.onIncrement()}>+1</button>
</div>
)
}
}
- 2.Function.prototype.bind()
利用ES5中的bind方法星持,將事件處理程序中的this與組件實例綁定到一起
class App extends React.Component{
state = {
count: 0,
}
constructor() {
super()
this.onIncrement = this.onIncrement.bind(this)
}
onIncrement() {
this.setState({
count: this.state.count + 1
})
}
render(){
return(
<div>
<h1>計數(shù)器:{this.state.count}</h1>
//箭頭函數(shù)中的this指向外部環(huán)境抢埋,此處為:render()方法
<button onClick={this.onIncrement}>+1</button>
</div>
)
}
}
- 3.class實例方法,推薦使用
利用箭頭函數(shù)形式的class實例方法。注意:該語法是實驗性語法揪垄,但是鲤屡,由于babel的存在可以直接使用
class App extends React.Component{
state = {
count: 0,
}
onIncrement = () => {
this.setState({
count: this.state.count + 1
})
}
render(){
return(
<div>
<h1>計數(shù)器:{this.state.count}</h1>
<button onClick={this.onIncrement}>+1</button>
</div>
)
}
}
15.表單處理
- 1.受控組件
- 由state的值來控制表單元素的值
- 步驟:
- 1.在state中添加一個狀態(tài),作為表單元素的value值(控制表單元素值的來源)
- 2.給表單元素綁定change事件福侈,將表單元素的值設置為state的值(控制表單元素的變化)
class App extends React.Component{
state = {txt:''}
handleChange = e => {
this.setState({
txt:e.target.value
})
}
render(){
return(
<div>
<input type="text" value={this.state.txt} onChange={this.handleChange}>+1</input>
</div>
)
}
}
- 2.多表單元素優(yōu)化
- 1.給表單增加name屬性,名稱與state相同
- 2.根據(jù)表單元素類型獲取對應值
- 3.在change事件處理程序中通過[name]來修改對應的state
<input name="txt" type="text" value={this.state.txt} onChange={this.handleChange}></input>
//根據(jù)表單元素類型獲取值
const value = target.type === "checkbox"
? target.checked
: target.value
//根據(jù)name設置對應state
this.setState({
[name]: value
})
- 3.非受控組件
借助于ref卢未,使用原生DOM方式來獲取表單元素值
ref作用:獲取DOM或組件
使用步驟: - 1.調(diào)用React.createRef()方法創(chuàng)建一個ref對象
construction(){
super()
this.txtRef = React.reacteRef()
}
- 2.將創(chuàng)建好的ref對象添加到文本框中
<input type="text" ref={this.txtRef} />
- 3.通過ref對象獲取到文本框的值
console.log(this.txtRef.current.value)
16.組件的props
- 組件是封閉的肪凛,要接收外部數(shù)據(jù)應該通過props來實現(xiàn)
- props的作用:接收傳遞給組件的數(shù)據(jù)
- 傳遞數(shù)據(jù):給組件標簽添加屬性
- 接收數(shù)據(jù):函數(shù)組件通過參數(shù)props接收數(shù)據(jù),類組件通過this.props接收數(shù)據(jù)
父傳子
特點: - 可以給組件傳遞任意類型的數(shù)據(jù)
- props是只讀的對象辽社,只能讀取屬性的值伟墙,無法修改對象
// 2.接收數(shù)據(jù)
const Hello = (props) => {
console.log(props)
props.fn()
// 修改props的值:錯誤演示!!!!!!!
// props.name = '123'
return(
<div>
<h1>props:{props.name}</h1>
{props.tag}
</div>
)
}
// 1.傳遞數(shù)據(jù)
ReactDOM.render(<Hello name="jack" age={19} fn={()=>console.log('這是一個函數(shù)')} tag={<p>這是一個p標簽</p>} />,document.getElementById('root'))
// 2.接收數(shù)據(jù)
class Hello extends React.Component{
constructor(props){
super(props)
console.log(props)
props.fn()
}
render(){
return(
<div>
<h1>props:{this.props.age}</h1>
</div>
)
}
}
// 1.傳遞數(shù)據(jù)
ReactDOM.render(<Hello name="rose" age={19} color={['red','green','blue']} />,document.getElementById('root'))
- 注意:使用類組件時,如果寫了構造函數(shù)滴铅,應該將props傳遞給super(),否則戳葵,無法在構造函數(shù)constrauctor中獲取到props!
class Hello extends React.Component{
//推薦將props傳遞給父類構=構造函數(shù)
constrauctor(props){
super(props)
}
render(){
return (
<div>
<h1>props:{this.props.age}</h1>
</div>
)
}
}
子傳父
- 1.父組件提供一個回調(diào)函數(shù)(用于接收數(shù)據(jù))
- 2.自組件調(diào)用傳入的函數(shù)
兄弟組件傳遞
狀態(tài)提升汉匙,由兄弟組件共同的父組件管理狀態(tài)
17.Context
跨組件傳遞數(shù)據(jù)
使用步驟:
- 2.自組件調(diào)用傳入的函數(shù)
- 1.調(diào)用React.createCcontext()創(chuàng)建Provider(提供數(shù)據(jù))和Consumer(消費數(shù)據(jù))兩個組件
const { Provider, Consumer } = React.createContext()
- 2.使用Provider組件作為父節(jié)點
- 3.設置value屬性拱烁,表示要傳遞的數(shù)據(jù)
<Provider value="pink">
- 4.調(diào)用Consumer組件接收數(shù)據(jù)
<Consumer>
{ data => <span>data參數(shù)表示接收到的數(shù)據(jù) -- { data }</span> }
</Consumer>
18.props深入
18.1 children屬性:表示組件標簽的子節(jié)點,當組件標簽有子節(jié)點時噩翠,props就會有該屬性
function Hello (props){
return(
<div>
組件的子節(jié)點:{ props.chihldren }
</div>
)
}
< Hello>我是子節(jié)點</Hello>
18.2 props校驗:允許在創(chuàng)建組件的時候戏自,就指定props的類型、格式等
- 對于組件來說伤锚,props是外來的擅笔,無法保證組件使用者傳入什么格式的數(shù)據(jù)
- 傳入到的數(shù)據(jù)格式不對,可能會導致報錯
- 作用:捕獲使用組件時因為props導致的錯誤屯援,給出明確的錯誤提示猛们,增加組件的健壯性
App.propTypes = {
colors: PropTypes.array
}
使用步驟:
- 安裝包prop-types(yarn add prop-types/npm i prop-types)
- 導入prop-types包
import PropTypes from 'prop-types'
- 使用 組件名.propTypes = {} 來給組件的props添加校驗規(guī)則
約束規(guī)則:
1.常見類型:array、bool狞洋、func弯淘、number、object徘铝、string
2.react元素類型:element
3.必填項:isRequired
4.特定結構的對象:shape({})
- 使用 組件名.propTypes = {} 來給組件的props添加校驗規(guī)則
//添加props校驗
//屬性a的類型 數(shù)值
//屬性fn的類型 函數(shù)并且為必填項
//屬性tag的類型 React元素(element)
//屬性filter的類型 對象({area: '上海', price: 1999})
App,propTypes = {
a: Proptypes.number,
fn: Proptypes.func.isRequired,
tag: Proptypes.element,
filter: Proptypes.shape({
area: Proptypes.string,
price: Proptypes.number
})
}
18.3 props默認值:未傳入props時生效
App.defaultProps = {
pageSize:0
}
- 組件的生命周期
19.1 意義:組件的生命周期有助于理解組件的運行方式耳胎,完成更復雜的組件功能,分析組件錯誤原因等
組件的生命周期:組件從被創(chuàng)建到掛載到頁面中運行惕它,再到組件不用卸載的過程
生命周期的每個階段總是伴隨著一些方法調(diào)用怕午,這些方法就是生命周期的鉤子函數(shù)
鉤子函數(shù) 的作用:為開發(fā)人員在不同階段操作組件提供了時機
只有 類組件 才有生命周期
19.2 生命周期的三個階段
-
創(chuàng)建階段(掛在階段)
執(zhí)行時機:組件創(chuàng)建時(頁面加載時)
執(zhí)行順序:
-
-
2.更新階段
執(zhí)行時機:1.setState() 2.forceUpdate() 3.組件接收到新的props
說明:以上三者任意一種變化,組件就會重新渲染
執(zhí)行順序:
在componentDidUpdate中調(diào)用this.setState時需要加if條件語句判斷
//比較更新前后的props是否相同淹魄,來決定是否重新渲染組件
if(prevProps.count !== this.props.count){
this.setState({})
//或者發(fā)送ajax請求的代碼
}
-
卸載階段
執(zhí)行時機:組件從頁面消失
-
- render-props和高階組件
20.1 React組件復用
復用什么:1.state 2. 操作state的方法(組件狀態(tài)邏輯)
兩種方式:1.render props模式 2. 高階組件(HOC)
- .render props模式
//調(diào)用
<Mouse render={(mouse) =>{
... ...
}}></Mouse>
//組件內(nèi)部
render(){
return this.props.render(this.state)
}
children代替render屬性
//調(diào)用
<Mouse>{() => {
... ...
}}<Mouse>
//組件內(nèi)部
this.props.children(this.state)
組件優(yōu)化:
//添加校驗
Mouse.propsTypes = {
children: PropTypes.func.isRequired
}
代碼優(yōu)化:
1.給render props模式添加props校驗
2.應該在組件卸載時郁惜,解除mousemove事件綁定
Mouse.propsTypes = {
children: PropTypes.func.isRequired
}
componentWillUnmount(){
window.removeEventListener('mousemove',this.handleMouseMove)
}
20.2高階組件
目的:實現(xiàn)狀態(tài)邏輯復用
使用步驟:
1.創(chuàng)建一個函數(shù),名稱約定以with開頭
2.指定函數(shù)參數(shù),參數(shù)應該以大寫字母開頭(作為要渲染的組件)
3在函數(shù)內(nèi)部創(chuàng)建一個類組件兆蕉,提供復用的狀態(tài)邏輯代碼羽戒,并返回
4.在該組件中,渲染參數(shù)組件虎韵,同時將狀態(tài)通過prop傳遞給參數(shù)組件
5.調(diào)用改高階組件易稠,傳入要增強的組件,通過返回值拿到增強后的組件包蓝,并將其渲染到頁面中
function withMouse(WrappedComponent){
class Mouse extends React.Component {}
return Mouse
}
//Mouse組件中的render方法
return <WrappedComponent {...this.state} />
//創(chuàng)建高階組件
function withMouse(WrappedComponent){
//該組件提供復用的狀態(tài)邏輯
class Mouse extends React.Component{
state:{
x:0,
y:0
}
handleMouseMove = e => {
this.setState({
x:e.clientX,
y:e.clientY
})
}
// 控制鼠標狀態(tài)的邏輯
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
componentWillUnmount(){
window.removeEventListener('mousemove',this.handleMouseMove)
}
render(){
return <WrappedComponent {...this.state} {...this.props} />
}
}
//設置displayName
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
return Mouse
}
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
//用來測試高階組件
const Position = props =>{
return (
<p>
鼠標當前位置:(x:{props.x},y:{props.y})
</p>
)
}
//獲取增強后的組件:
const MousePosition = withMouse(Position)
class App extends React.Component{
render() {
return(
<div>
<h1>高階組件</h1>
<MousePosition a='1' />
<div/>
)
}
}
設置displayName
- 使用高階組件存在的問題:得到的兩個組件名稱相同
- 原因:默認情況下驶社,React使用組件名稱作為displayName
- 解決方式:為高階組件設置displayName便于調(diào)試時區(qū)分不同的組件
- displayName的作用:用于設置調(diào)試信息(React Developer Tools信息)
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
傳props:
render(){
return <WrappedComponent {...this.state} {...this.props} />
}
//組件MousePosition 傳入的參數(shù)就不會丟失了
21.setState說明
21.1異步更新
調(diào)用多次setState,只會觸發(fā)一次render渲染
21.2推薦語法
this.setState((state,props)=>{
return{
count:state.count + 1
}
})
回調(diào)函數(shù)形式的setState更新也是異步更新测萎,立刻打印state亡电,結果還是1。
連續(xù)兩次更新state加1硅瞧,第二次是在第一次基礎上加一的份乒,即第二次拿到的值是2,第二次更新是在2上?1
21.3
setState第二個參數(shù)
- 場景:在狀態(tài)更新(頁面完成重新渲染)后立即執(zhí)行
- 語法:setState(updater,[,callback])
this.setState(
(state,props)=>{},
()=>{console.log('這個回調(diào)函數(shù)會在狀態(tài)跟新后立即執(zhí)行')}
)