這是我參與8月更文挑戰的第3天,活動詳情查看:8月更文挑戰vue
本文做爲本人學習總結之用,同時分享給你們,適合入門的react小白
由於我的技術有限,若是有發現錯誤或存在疑問之處,歡迎指出或指點!不勝感謝!react
動做的對象git
包含兩個屬性github
編碼:ajax
//該模塊是用於定義action對象中type類型的常量值,目的只有一個:便於管理的同時防止單詞寫錯
import {ADD_PERSON} from '../constant'
//專門爲組件生成action對象
export const addPerson = data => ({type:ADD_PERSON,data})
複製代碼
const initState = xx
export default function xxxReducer(preState =initState, action) {
const {type ,data} = action
switch (type) {
case JIA:
return preState+1
default :
return preState
}
}
複製代碼
//引入createStore,專門用於建立redux中最爲核心的store對象
import {createStore} from 'redux'
//引入爲Count組件服務的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)
複製代碼
此對象的功能?npm
getState()
: 獲得state
dispatch(action)
: 分發action
, 觸發reducer
調用, 產生新的state
subscribe(listener)
: 註冊監聽, 當產生了新的state
時, 自動調用componentDidMount(){
//檢測redux中狀態的變化,只要變化,就調用render
store.subscribe(()=>{
this.setState({})
})
}
store.dispatch(createIncrementAction(value*1))
store.getState()
複製代碼
做用:建立包含指定reducer的store對象redux
store.getState()
store.dispatch({type:'INCREMENT', number})
store.subscribe(render)
做用:應用上基於redux的中間件(插件庫)api
做用:合併多個reducer函數數組
1. redux默認是不能進行異步處理的,緩存
2. 某些時候應用中須要在redux 中執行異步任務(ajax, 定時器)
必須下載插件:
npm install --save redux-thunk
在store.js文件中引入redux-thunk,用於支持異步action
import thunk from 'redux-thunk'
UI組件
容器組件
import {Provider} from 'react-redux'
<Provider store={store}>
<App/>
</Provider>
複製代碼
//引入Count的UI組件
import CountUI from '../../components/Count'
//引入action
import {
createIncrementAction,
} from '../../redux/count_action'
//引入connect用於鏈接UI組件與redux
import {connect} from 'react-redux'
/* 1.mapStateToProps函數返回的是一個對象; 2.返回的對象中的key就做爲傳遞給UI組件props的key,value就做爲傳遞給UI組件props的value 3.mapStateToProps用於傳遞狀態 */
function mapStateToProps(state){
return {count:state}
}
/* 1.mapDispatchToProps函數返回的是一個對象; 2.返回的對象中的key就做爲傳遞給UI組件props的key,value就做爲傳遞給UI組件props的value 3.mapDispatchToProps用於傳遞操做狀態的方法 */
function mapDispatchToProps(dispatch){
return {
jia:number => dispatch(createIncrementAction(number))
}
}
//使用connect()()建立並暴露一個Count的容器組件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
//簡寫
export default connect(
state => ({count:state}),
{jia:createIncrementAction}
)(Count)
複製代碼
setState(stateChange, [callback])
------對象式的setState
1.stateChange爲狀態改變對象(該對象能夠體現出狀態的更改)
2.callback是可選的回調函數, 它在狀態更新完畢、界面也更新後(render調用後)才被調用
複製代碼
setState(updater, [callback])
------函數式的setState
1.updater爲返回stateChange對象的函數。
2.updater能夠接收到state和props。
3.callback是可選的回調函數, 它在狀態更新、界面也更新後(render調用後)才被調用。
複製代碼
總結:
//對象式的setState
//1.獲取原來的count值
const {count} = this.state
//2.更新狀態
this.setState({count:count+1},()=>{
console.log(this.state.count);
})
//console.log('12行的輸出',this.state.count); //0
//函數式的setState
this.setState( (state,props) => ({count:state.count+1}),() =>{
console.log(state.count)
})
複製代碼
//1.經過React的lazy函數配合import()函數動態加載路由組件 ===> 路由組件代碼會被分開打包
const Login = lazy(()=>import('@/pages/Login'))
//2.經過<Suspense>指定在加載獲得路由打包文件前顯示一個自定義loading界面
fallback包裹一個通常組件也能夠
<Suspense fallback={<h1>loading.....</h1>}>
<Switch> <Route path="/xxx" component={Xxxx}/> <Redirect to="/login"/> </Switch>
</Suspense>
複製代碼
React Hook/Hooks是什麼?
三個經常使用的Hook
State Hook: React.useState()
Effect Hook: React.useEffect()
Ref Hook: React.useRef()
State Hook
setXxx()2種寫法:
Effect Hook
useEffect(() => {
// 在此能夠執行任何帶反作用操做
return () => { // 在組件卸載前執行
// 在此作一些收尾工做, 好比清除定時器/取消訂閱等
}
}, [stateValue]) // 若是指定的是[], 回調函數只會在第一次render()後執行
複製代碼
能夠把 useEffect Hook 看作以下三個函數的組合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
Ref Hook
function Demo(){
const [count,setCount] = React.useState(0)
const myRef = React.useRef()
React.useEffect(()=>{
let timer = setInterval(()=>{
setCount(count => count+1 )
},1000)
return ()=>{
clearInterval(timer)
}
},[])
//加的回調
function add(){
//setCount(count+1) //第一種寫法
setCount(count => count+1 )
}
//提示輸入的回調
function show(){
alert(myRef.current.value)
}
//卸載組件的回調
function unmount(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
return (
<div> <input type="text" ref={myRef}/> <h2>當前求和爲:{count}</h2> <button onClick={add}>點我+1</button> <button onClick={unmount}>卸載組件</button> <button onClick={show}>點我提示數據</button> </div>
)
}
複製代碼
做用:能夠不用必須有一個真實的DOM根標籤了
<Fragment key={1}> //能參與遍歷
<input type="text"/>
<input type="text"/>
</Fragment>
或者
<>
<input type="text"/>
<input type="text"/>
</>
複製代碼
一種組件間通訊方式, 經常使用於【祖組件】與【後代組件】間通訊
const XxxContext = React.createContext()
<xxxContext.Provider value={數據}>子組件</xxxContext.Provider>
後代組件讀取數據:
//第一種方式:僅適用於類組件
static contextType = xxxContext // 聲明接收context
this.context // 讀取context中的value數據
//第二種方式: 函數組件與類組件均可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value數據
要顯示的內容
)
}
</xxxContext.Consumer>
複製代碼
注意:在應用開發中通常不用context, 通常都它的封裝react插件
//建立Context對象
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext
export default class A extends Component {
state = {username:'tom',age:18}
render() {
const {username,age} = this.state
return (
<div className="parent"> <h3>我是A組件</h3> <h4>個人用戶名是:{username}</h4> <Provider value={{username,age}}> <B/> </Provider> </div>
)
}
}
class B extends Component {
render() {
return (
<div className="child"> <h3>我是B組件</h3> <C/> </div>
)
}
}
/* class C extends Component { //聲明接收context static contextType = MyContext render() { const {username,age} = this.context return ( <div className="grand"> <h3>我是C組件</h3> <h4>我從A組件接收到的用戶名:{username},年齡是{age}</h4> </div> ) } } */
function C(){
return (
<div className="grand"> <h3>我是C組件</h3> <h4>我從A組件接收到的用戶名: <Consumer> {value => `${value.username},年齡是${value.age}`} </Consumer> </h4> </div>
)
}
複製代碼
項目的2個問題
只要執行setState(),即便不改變狀態數據, 組件也會從新render()
只當前組件從新render(), 就會自動從新render子組件 ==> 效率低
效率高的作法:
只有當組件的state或props數據發生改變時才從新render()
緣由:
Component中的shouldComponentUpdate()老是返回true
解決:
辦法1:
重寫shouldComponentUpdate()方法
比較新舊state或props數據, 若是有變化才返回true, 若是沒有返回false
複製代碼
辦法2:
使用PureComponent
PureComponent重寫了shouldComponentUpdate(), 只有state或props數據有變化才返回true
注意:
只是進行state和props數據的淺比較, 若是隻是數據對象內部數據變了, 返回false
不要直接修改state數據, 而是要產生新數據
複製代碼
項目中通常使用PureComponent來優
import React, { PureComponent } from 'react'
export default class Parent extends PureComponent {
state = {carName:"奔馳c36",stus:['小張','小李','小王']}
addStu = ()=>{
const {stus} = this.state
this.setState({stus:['小劉',...stus]})
}
changeCar = ()=>{
this.setState({carName:'邁巴赫'})
}
/* shouldComponentUpdate(nextProps,nextState){ // console.log(this.props,this.state); //目前的props和state // console.log(nextProps,nextState); //接下要變化的目標props,目標state return !this.state.carName === nextState.carName } */
render() {
console.log('Parent---render');
const {carName} = this.state
return (
<div className="parent"> <h3>我是Parent組件</h3> {this.state.stus} <span>個人車名字是:{carName}</span><br/> <button onClick={this.changeCar}>點我換車</button> <button onClick={this.addStu}>添加一個小劉</button> <Child carName="奧拓"/> </div>
)
}
}
class Child extends PureComponent {
/* shouldComponentUpdate(nextProps,nextState){ console.log(this.props,this.state); //目前的props和state console.log(nextProps,nextState); //接下要變化的目標props,目標state return !this.props.carName === nextProps.carName } */
render() {
console.log('Child---render');
return (
<div className="child"> <h3>我是Child組件</h3> <span>我接到的車是:{this.props.carName}</span> </div>
)
}
}
複製代碼
\
如何向組件內部動態傳入帶內容的結構(標籤)?
Vue中:
使用slot技術, 也就是經過組件標籤體傳入結構 <AA><BB/></AA>
React中:
- 使用children props: 經過組件標籤體傳入結構
- 使用render props: 經過組件標籤屬性傳入結構, 通常用render函數屬性
複製代碼
children props
<A>
<B>xxxx</B>
</A>
{this.props.children}
複製代碼
問題: 若是B組件須要A組件內的數據, ==> 作不到
render props
<A render={(data) => <C data={data}></C>}></A>
A組件: {this.props.render(內部state數據)}
C組件: 讀取A組件傳入的數據顯示 {this.props.data}
理解:
錯誤邊界:用來捕獲後代組件錯誤,渲染出備用頁面
特色:
只能捕獲後代組件生命週期產生的錯誤,不能捕獲本身組件產生的錯誤和其餘組件在合成事件、定時器中產生的錯誤
使用方式:
getDerivedStateFromError配合componentDidCatch
// 生命週期函數,一旦後臺組件報錯,就會觸發
//當Parent的子組件出現報錯時候,會觸發getDerivedStateFromError調用,並攜帶錯誤信息
static getDerivedStateFromError(error){
console.log('@@@',error);
return {hasError:error}
}
componentDidCatch(){
console.log('此處統計錯誤,反饋給服務器,用於通知編碼人員進行bug的解決');
}
複製代碼