React 基礎與實戰
ReactJs基礎與實戰
前言
剛入前端是先接觸的 React,後面遇到的專案都是 Vue,就把 React 忘了個徹底,直到現在要開始寫 Taro 了~,這篇就是根據當時學習 React 過程記錄下的筆記,因為當時筆記記的非常凌亂,再加上有道雲筆記的目錄非常不友好,所以重新整理成了這篇釋出出來,很久沒寫過,所以如果存在錯誤的地方請留言,我會及時更改~
github地址:ReactJs基礎與實戰.md 喜歡的點個 Star 吧~
環境搭建
npm install -g create-react-app //安裝 create-react-app + 專案名稱 //建立專案 npm start //重啟
設計思想
在React的官方部落格中明確闡述了 React 不是一個 MVC 框架,而是一個用於構建元件化 UI 的庫,是一個前端介面開發工具。所以頂多算是 MVC 中的 V(view)。React 並沒有重複造輪子,而是有很多顛覆性的創新,具體的特性如下:
JSX寫法
JSX就是 Javascript 和 XML 結合的一種格式。React 發明了 JSX,利用 HTML 語法來建立虛擬 DOM。當遇到<,JSX 就當 HTML 解析,遇到{就當 JavaScript 解析。
import './css/index.css' class App extends Component { render() { var myname = 'anna' //不是狀態 var stylyobj = { background: 'red', fontSize: '30px' } //將來 生命週期 return ( <div> {10 + 20}--{myname} {10 > 20 ? 1 : 0} <div style={stylyobj}>111111</div> <div style={{ background: 'yellow' }}>111111</div> // 16版本之前 class 不支援 必須寫成className // 16版本之後 class支援 ,但是會報警告 <div className='active' >33333</div> <div id='box' >33333</div> </div> ) } }
元件寫法
1、class 類式元件
import React from 'react' import { Component} from 'react' class Hello extends Component { render() { //將來 生命週期 return ( <div>111111 <ul> <li>1111</li> <li>2222</li> <Child1/> <Child2/> <Child3/> </ul> </div> ) } } //Component ===>React.Component 下面的 可以直接被引入 class Child1 extends Component{ render(){ return <div>child1</div> } }
2、function 函式式元件
React16.8 之前, 函式式元件不支援狀態
React16.8 之後, React Hooks 支援狀態和屬性
function Child2(){
return (<div>child2
<span>22222</span>
</div>)
}
const Child3=()=><div>child3</div>
export default Hello
事件的四種寫法
寫法一: 程式碼量少的情況,可直接在標籤裡面寫
// 獲取到輸入框的value值
<input type = 'text' ref = 'mytext' />
<button onClick={() => {
console.log('onclick', this.refs.mytext)
}}>add</button>
寫法二:方便傳參,改變this執行
{/* 注意這裡不能加小括號 觸發的時候會自動呼叫 。如果加小括號 ,= 函式返回值再呼叫*/}
<button onClick={this.handleAdd2.bind(this)}>add2</button>
//寫在 render 外面
handleAdd2(){
console.log('click22222', this.refs.mytext.value)
}
寫法三:箭頭函式 無法傳參,但是畢竟方便
<button onClick={this.handleAdd3}>add3</button>
handleAdd3=()=>{
console.log(this.refs.mytext.value)
}
寫法四:組合寫法
<button onClick={() => {
this.handleAdd3('aaa','bbbb')
}}>add4</button>
handleAdd3=(x,y)=>{
console.log(x,y,'click22222', this.refs.mytext.value)
}
輸入框改變獲取輸入框的值:
<div>
<input type="test" onChange={(evt)=>{
console.log(evt.target.value)
}}/>
賦值給輸入框
value={this.state.mytext}
改變this指向
bind call apply區別
call:可以傳入多個引數,改變this指向後立刻呼叫函式
apply:可以傳入陣列 ,改變this指向後立刻呼叫函式
bind:改變this指向後,可以傳入多個引數,返回的是函式 不會立即呼叫
var obj1={
name:'obj1',
getName(){
console.log(this.name)
}
}
var obj2 = {
name: 'obj2',
getName() {
console.log(this.name)
}
}
// obj1.getName()//obj1
// 改變this指向,立即指向方法
// obj1.getName.call(obj2,'aaa','bbbb','cccc')//obj2
//obj1.getName.apply(obj2,['aaa','bbb','ccc'])//obj2
// 改變this指向,但是需要手動執行方法
//obj1.getName.bind(obj2,'aaa','bbb','ccc')()
初始化狀態和修改狀態
狀態(state)
export default class App extends Component {
state={
myname:'4567'
}
render() {
return (
<div>
{this.state.myname}
</div>
)
}
}
修改狀態(setState)
同步過程
<button onClick={this.handkeClick}>click</button>
//直接修改狀態
handkeClick=()=>{
this.setState({
myname:'xiaoming',
myage:'18'
})
}
非同步過程
第一種寫法:
接收兩個引數:
第一個引數是物件,修改的狀態值
第二個引數 能夠等待dom樹更新完之後執行
this.setState({
myname:'xiaoming'
},()=>{
console.log('1',this.state.myname)
})
之後發生什麼?
//1.虛擬dom建立
//2.diff對比
第二種寫法:
可以獲取到上個狀態值(prevState) 必須有返回值
//1. 簡寫
this.setState((prevState)=>({
count: prevState.count+1
}))
// 2. 完整寫法
this.setState((prevState)=>{{
count: prevState.count+1
}})
setState 何時同步,何時非同步,為什麼會這樣, React 如何去控制同步非同步?
想了解的可以看這篇React 中setState更新state何時同步何時非同步?
遍歷
寫法一:
{ this.state.datalist.map(item => <li key={item}>{item}</li>) }
寫法二:
var newlist = this.state.datalist.map(item => <li key={item}>{item}</li>)
{newlist} //使用變數
通訊
父傳子 通過屬性(props)
父:
//需要 {} 包住才是 js 不然是字串
<Navbar mytitle='home' myshow={false}></Navbar>
子:可以直接通過this.props.屬性名獲取
{this.props.mytitle}
屬性簡寫:
var obj={
mytitle:'測試',
myshow:false
}
<Navbar {...obj}></Navbar>
屬性驗證
Navbar.propTypes 可以訪問到
import MyPropTypes from 'prop-types'; //提供驗證資料型別的方法,必須交給MyPropTypes模組方法進行處理驗證
class Navbar extends Component {
static propTypes = {
myshow: MyPropTypes.bool,
};
}
預設屬性
static defaultProps = {
myshow: true
}
子傳父 通過事件
父:傳了一個回撥函式過去
<Navbar onEvent={() => {
this.setState({
isShow: !this.state.isShow
})
}}></Navbar>
子 :收到這個回撥函式之後 直接呼叫
<button onClick={this.handleClick}>hide/show</button>
handleClick = () => {
this.props.onEvent()
}
Ref
Ref 你可以用來繫結到 render() 輸出的任何元件上。
這個特殊的屬性允許你引用 render() 返回的相應的支撐例項( backing instance )。這樣就可以確保在任何時間總是拿到正確的例項。
父元件:可一獲取到Input元件的例項,並且對他內部的狀態值進行修改
<Input ref='mytext' />
<button onClick={
this.handleClick
}>add</button>
handleClick = () => {
console.log(this.refs.mytext.state.mytext)// 拿到值
this.refs.mytext.reset() // 清空輸入框
}
子元件:
class Input extends Component {
state = {
mytext: ''
}
reset = () => {
this.setState({
mytext: ''
})
}
<div>
<div>others input</div>
<input value={this.state.mytext} type='text' style={{ background: 'yellow' }} onChange={(ev) => {
this.setState({
mytext: ev.target.value
})
釋出訂閱模式
自己寫一個釋出訂閱模式來傳遞資訊
事件匯流排:用來觀察訂閱者和釋出者,如果發現釋出者傳送了資訊,將資訊立刻傳送給訂閱者
const EventChannel = {
list: [],
subscribe(callback) {
this.list.push(callback)
},
dispatch(data) {
this.list.forEach(item => {
item(data)
})
}
}
訂閱者:把自身的回撥儲存在事件匯流排,事件匯流排遍歷呼叫 釋出者呼叫釋出方法可以傳入釋出者自身的引數 訂閱者即可獲取到
class Child3 extends Component {
// 建立成功 ,dom掛載完成
componentDidMount() {
observer.subscribe((data) => {
console.log('child3定義的callback', data)
})
// console.log('componentDidMount', '呼叫訂閱方法', observer.subscribe())
}
render() {
return <div style={{ background: 'blue' }}>我是微信使用者</div>
}
}
class Child3 extends Component {
// 建立成功 ,dom掛載完成
componentDidMount() {
observer.subscribe((data) => {
console.log('child3定義的callback', data)
})
// console.log('componentDidMount', '呼叫訂閱方法', observer.subscribe())
}
render() {
return <div style={{ background: 'blue' }}>我是微信使用者</div>
}
}
釋出者:呼叫事件匯流排的釋出方法
釋出方法:把訂閱者的回撥遍歷出來 然後呼叫 遍歷中間可以傳入自己的引數
class Child2 extends Component {
render() {
return <div style={{ background: 'red' }}>公眾號釋出者
<button onClick={this.handleClick}>釋出</button>
</div>
}
handleClick=()=>{
EventChannel.dispatch('child2的問候')
}
}
context 通訊
基地:提供自己的狀態 以及修改狀態的方法
基地程式碼:
export default class App extends Component {
state = {
text: '私人服務'
}
changeState=(data)=>{
this.setState({
text: data
})
}
render() {
return (
<GlobalContext.Provider value={{
sms: '簡訊服務',
call: '電話服務',
text: this.state.text,
changeState:this.changeState
}}>
<div>
<Child1></Child1>
</div>
</GlobalContext.Provider >
)
}
}
通訊者:呼叫基地修改的方法,傳入自己的資訊
class Child2 extends Component {
render() {
return <GlobalContext.Consumer>
{context => (
<div style={{ background: 'blue' }}>child2--{context.call}
<button onClick={() => this.handClick(context)}>child2通訊</button>
</div>
)
}
</GlobalContext.Consumer>
}
handClick = (context) => {
context.changeState('來自child2的問候')
console.log(context)
}
}
其他通訊者:一旦狀態改變了 ,可以馬上獲取到
class Child1 extends Component {
render() {
return <GlobalContext.Consumer>
{context => (
<div style={{ background: 'yellow' }}>child1--{context.text}</div>
)
}
</GlobalContext.Consumer>
}
}
生命週期
一個元件會按照順序依次經歷以下的三個階段:初始化階段、執行中階段、銷燬階段
其中的三個生命週期即將被廢棄,不建議使用,增加了兩個新的生命週期替代~
初始化階段
componentWillMount
render 之前最後一次修改狀態的機會,在渲染前呼叫,在客戶端也在服務端,
即將被廢棄,不建議使用,17版本之後必須加上 UNSAFE_ 才可以工作(UNSAFE_componentWillMount),
廢棄理由:
在 ssr 中這個方法將會被多次呼叫,所以會重複觸發多遍,同時在這裡如果繫結事件,將無法解綁,導致記憶體洩漏,變得不夠安全高效逐步廢棄
UNSAFE_componentWillMount(){
console.log('componentWillMount','ajax',)
}
render
只能訪問 this.props 和 this.state,不允許修改狀態和 dom,在生命週期中會被多次呼叫。
componentDidMount
成功 render 並渲染完成真實 dom 之後觸發,可以修改 dom, 一般請求資料會寫在這個生命週期
componentDidMount() {
console.log('componentDidMount', 'ajax 繫結')
fetch("/test.json").then(res=>res.json()).then(res=>{
console.log(res.data)
this.setState({
list: res.data.films
})
})
}
執行中階段
componentWillReceiveProps
父元件修改屬性觸發(子元件使用) 會走多次 可以在這裡獲取到id
即將被廢棄,不建議使用,17版本之後必須加上 UNSAFE_ 才可以工作(UNSAFE_componentWillReceiveProps)
廢棄理由:
外部元件多次頻繁更新傳入多次不同的props,會導致不必要的非同步請求
這個生命週期有新的生命週期替換—— **getDerivedStateFromProps ** 可以看後面的介紹~
// 會走多次 可以在這裡獲取到id 更新 mount 只會走一次
UNSAFE_componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps')
console.log('獲取到ajax資料', nextProps.myname)
}
shouldComponentUpdate
**返回 false 會阻止 render 呼叫 **
可以做效能調優函式 可以獲取到新的狀態和老的狀態然後對比狀態,如果狀態沒有改變不重新渲染
shouldComponentUpdate(nextProps, nextState) {
// 效能調優函式 //新的狀態和老的狀態
console.log('shouldComponentUpdate',this.state.myname,nextState.myname)
if (this.state.myname !== nextState.myname){
return true //返回 true 會自動渲染 這個是手動自己去對比 dom 是否發生改變再去呼叫 render
// react有自動優化能力 —— PureComponent(看後面~)
}
return false //false 不會重新渲染
}
componentWillUpdate
不允許修改屬性和狀態,會被觸發多次,即將被廢
廢棄理由:
更新前記錄 DOM 狀態,可能會做一些處理,與 componentDidUpdate 相隔時間如果太長,會導致狀態不可信,有新的生命週期—— getSnapshotBeforeUpdate 替換
UNSAFE_componentWillUpdate(){
console.log('componentWillUpdate')
}
render
只能訪問 this.props 和 this.state,不允許修改狀態和 dom,在生命週期中會被多次呼叫
componentDidUpdate
在元件完成更新後立即呼叫,在初始化時不會被呼叫,可以修改dom
銷燬階段
componentWillUnmount
在元件從 DOM 中移除之前立刻被呼叫,在刪除元件之前進行清理操作,比如計時器和事件監聽器。
新增加的兩個生命週期
getDerivedStateFromProps
同步作用:
1.可以改老狀態
2.不管是初始化 或者更新 都可以拿到父元件屬性
componentWillReceiveProps(nextProps) //改變才會獲取到屬性
// 裡面不能用this 必須用static
static getDerivedStateFromProps(nextprops, state) {
//初始化屬性 和更新屬性都可以獲取到
document.title = nextprops.mytitle
console.log(nextprops, state)
return null //狀態不改變
return {
myname: state.myname.substring(0, 1).toUpperCase()
} //返回一個新的狀態 可以在次修改狀態
}
非同步作用:
在getDerivedStateFromProps 中不可以做ajax請求,必須和其他生命週期配合使用
componentDidMount() {
console.log('發ajax', this.state.myid)
}
// componentWillReceiveProps() {
// console.log('發ajax')
// }
static getDerivedStateFromProps(nextprops, state) {
console.log('getDerivedStateFromProps','獲取到id值', nextprops.id)
return {
myid: nextprops.id
}
}
getSnapshotBeforeUpdate
可以在更新之前獲取到狀態
//data 接收到了getSnapshotBeforeUpdate的返回值
componentDidUpdate(prevProps, prevState,data) {
console.log('componentDidUpdate', data)
}
// 在render 生命之後 在已經更新完之前 可以準確獲取 返回之後的狀態
// componentDidUpdate第三個引數可以獲取到這個值
getSnapshotBeforeUpdate = (prevProps, prevState) => {
console.log('getSnapshotBeforeUpdate','獲取滾動條的位置')
return {
y:100
}
}
React 中效能優化的方案
1、shouldComponentUpdate
手動控制元件自身或者子元件是否需要更新,尤其子元件非常多的情況,不適合這個方案
2、PureComponent
PureComponent 是 React 提供的自動優化 ,會幫你比較新的 props 跟 舊的 props ,
取決於值是否相等(值相等,或者物件含有相同的屬性、且屬性值相等),決定 shouldComponentUpdate 返回true 或者 false,從而決定要不要呼叫 render function
優點:可以減少重複生命週期的執行 會自動對比虛擬dom等
缺點:如果你的state 或者 props '永遠都會變',那 PureComponent 並不會更加快,因為 shallowEqual 也需要花時間,元件如果需要實時更新 可以用 shouldComponentUpdate
import React, { Component, PureComponent } from 'react'
export default class App extends PureComponent {}
插槽
this.props.children 獲取到的是內容陣列
Child 元件:
const Child = (props)=>{
return <div>
child--{props.children}
</div>
}
使用這個元件:
export default function App() {
return (
<div>
{/* 插槽 */}
<Child>
<li>11111</li>
<li>222222</li>
</Child>
</div>
)
}
路由
安裝:
cnpm i --save react-router-dom
HashRouter模式下 多次點相同路徑會被警告
解決方案: 換成history模式 : BrowserRouter
寫法:
react-router-dom 4.5 版本寫法一致:
//
import {
HashRouter as Router, //路由外層需要包裹的元件 hash模式
// BrowserRouter(後端配置 history 模式)
Route ,//每個路由元件都需要此元件
}from 'react-router-dom'
import React from 'react'
import Home from '../views/home/Home'
import Login from '../views/login/Login'
// class BlogRouter extends Comment{
// }
** 函式式元件寫法:**
const BlogRouter=()=>(
<Router>
<Route path='/home' component={Home}/>
<Route path='/login' component={Login}/>
</Router>
)
export default BlogRouter
app.js
import BlogRouter from './router'
class App extends Component{
render(){
return(
<div>
<BlogRouter/>
</div>
)
}
}
export default App;
巢狀路由
巢狀路由兩種寫法:
1.在父元件中直接寫:
import { Route} from 'react-router-dom'
import Right from './Right'
import Role from './Role'
export default class Manage extends Component {
render() {
return (
<div>
<ul>
<li>許可權列表</li>
<li>角色列表</li>
<Route path='/right-manage/right' component={Right} />
<Route path='/right-manage/roles' component={Role} />
</ul>
</div>
)
}
}
- 在路由中寫:
import Manage from '../views/rightmanage/Manage'
import Right from '../views/rightmanage/Right'
import Role from '../views/rightmanage/Role'
<Route path='/right-manage' render={()=>
(<Manage>
<Switch>
<Route path='/right-manage/rights' component={Right} />
<Route path='/right-manage/roles' component={Role} />
<Redirect from='/right-manage' to='/right-manage/roles' />
</Switch>
</Manage>)
}/>
** 在父元件中留坑:**
{this.props.children}
路由重定向
** 一定要包 Switch ** 一旦匹配上就不會再繼續匹配 會直接跳出
import {
Route,
Redirect,//重定向
Switch//匹配到第一個符合條件路徑的元件,就停止了
} from 'react-router-dom'
export default class DashBorad extends Component {
render() {
return (
<div>
<div>頂部導航欄</div>
<Switch>
{/* 重定向 */}
<Redirect from='/' to='/home' exact />
<Route path='*' component={Notfind} />
</Switch>
</div>
)
}
}
跳轉頁面
路由渲染跳轉:
<Route path='/artic-manege/preview/:myid' component={Prebiew} />
程式設計式跳轉頁面:
this.props.history.push(`/artic-manege/preview/${id}`)
獲取到傳遞的值:
this.props.match.params.myid
高階元件(withRouter)
高階元件,獲取低階元件,生成高階元件 可以實現路由包裹跳轉清空等
有些元件沒有被 router 包圍,會獲取不到會有 this.props ,可以用高階元件進行包裹,然後獲取其this.props
import {withRouter} from 'react-router' //路由
//獲取到 this.props 跳轉至 home
this.props.history.push('/home')
export default withRouter(SideMenu)
獲取不到 this.props 解決方案
除了用高階元件的方法,還可以用父元件傳遞this.props給子元件
父元件:
annahistory={this.props.history}
子元件:
this.props.kerwinhitory.push(obj.key)
Redux
Redux 主要用作應用狀態的管理,即 Redux 用一個單獨的常量狀態樹(物件)保持這一整個應用的狀態,這個物件不能直接被改變。如果一些資料變化了,一個新的物件就會被建立(使用 action 和 reducers )
Redux 的工作流程:
同步寫法
store.js 檔案:
import {createStore} from 'redux'//createStore 方法建立一個store回想
// 建立一個reducer ,
//'修改狀態'(接收老狀態,修改的值,深複製之後,再返回一個新的狀態)
const reducer=(prevState={
// 設定一個初始值
iscollapsed:false
},action)=>{
console.log(action)
// 深複製一份新的把action裡面獲取的值 返回出去
var newstate={...prevState}
newstate.iscollapsed=payload
return newstate
}//只要狀態已返回,會自動更新
const store=createStore(reducer)
export default store
釋出者(釋出自己的狀態)
store.dispatch({
type:'mysideMenuCollapsed',
payload: iscollapsed
});//store 在action裡面可以獲取到釋出者的值
訂閱者(做出改變者,要獲取新的狀態)
componentDidMount() {
// 訂閱 注意一定要取消訂閱
this.unscribe= store.subscribe(()=>{
//store.getState() 這個可以獲取到新的狀態
console.log('有人通知我更新了',store.getState())
this.setState({
collapsed: store.getState().iscollapsed
})
})
}
componentWillUnmount(){
// 取消訂閱
this.unscribe()
}
非同步寫法
1、redux-Thunk
redux-thunk可以在actionCreator中返回一個函式,將函式執行,並傳入dispatch和getState兩個引數給這個函式,我們可以在任意時候dispatch
store.js:
import { createStore ,applyMiddleware} from 'redux'//createStore 方法建立一個store回想
import reduxThunk from 'redux-thunk'
// 建立一個reducer ,
//'修改狀態'(接收老狀態,修改的值,深複製之後,再返回一個新的狀態)
const reducer = (prevState = {
// 設定一個初始值
roleList:[]//角色側邊導航資料
}, action) => {
// 深複製
let { type, payload } = action
switch (type) {
case 'setRoleList':
var newstate = { ...prevState }
newstate.roleList= payload
return newstate
default :
return prevState
}
}//只要狀態已返回,會自動更新
// 預設 action 只能是普通物件{type:''}
// 建立store 順便應用中介軟體thunk 如果action是函式,我來處理
const store = createStore(reducer,applyMiddleware(reduxThunk))
export default store
訂閱者和釋出者案例:
role.js:
actionCreater = () => {
// middleware 解決非同步處理redux-thunk redux-promise
return (dispatch) => {
axios.get("http://localhost:8000/roles").then(res => {
console.log(res.data)
// 自己決定什麼時候傳送
dispatch({
type: 'setRoleList',
payload: res.data
})
})
}
}
componentDidMount() {
if (store.getState().roleList.length == 0) {
//發ajax
store.dispatch(this.actionCreater())
} else {
console.log('使用快取', store.getState().roleList)
this.setState({
datalist: store.getState().roleList
})
}
//資料改變了 訂閱獲取到新的資料
this.unscribe = store.subscribe(() => {
console.log("請求資料結束", store.getState().roleList)
this.setState({
datalist: store.getState().roleList
})
})
}
componentWillUnmount() {
// 取消訂閱
this.unscribe()
}
2、redux-promise
redux-promise可以在actionCreator中返回一個promise物件,他會等待成功後將成功後的結果派發出去
store.js
import reduxPromise from 'redux-promise'
import { createStore, applyMiddleware } from 'redux'//createStore 方法建立一個store回想
const reducer = (prevState = {
// 設定一個初始值
rightList: [],//角色側邊導航資料
}, action) => {
console.log(action)
// 深複製
let { type, payload } = action
switch (type) {
case 'setRightsList':
var newstate = { ...prevState }
newstate.rightList = payload
return newstate
default:
return prevState
}
}//只要狀態已返回,會自動更新
const store = createStore(reducer, applyMiddleware(reduxPromise))
export default store
訂閱者和釋出者案例:
role.js:
actionCreater = () => {
// 返回一個promise物件
return axios.get("http://localhost:8000/rights").then(res => {
return {
type: 'setRightsList',
payload: res.data
}
})
}
componentDidMount() {
if (store.getState().rightList.length == 0) {
//發ajax
store.dispatch(this.actionCreater()).then(data=>{
this.setState({
datalist: store.getState().rightList
})
})
} else {
console.log('使用快取', store.getState().rightList)
this.setState({
datalist: store.getState().rightList
})
}
}
核心API-Reducer
Reducer保證是純函式
純函式
1.對外界沒有副作用的函式
2.同樣的輸入,得到同樣的輸出
var myname='kerwin'
function test(myname){
myname='xiaoming'
}
test(myname)
非純函式:
var myname='kerwin'
function test(){
myname='xiaoming'
}
test()
拆分
可以將 Reduer 按照業務模組去拆分
store.js
import { createStore, applyMiddleware ,combineReducers} from 'redux'//createStore 方法建立一個store回想
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'
import collapseReducer from './reducers/collapseReducer'
import rightListReducer from './reducers/rightListReducer'
import roleListReducer from './reducers/roleListReducer'
const reducer = combineReducers({
iscollapsed: collapseReducer,
roleList: roleListReducer,
rightList: rightListReducer
})
const store = createStore(reducer, applyMiddleware(reduxThunk, reduxPromise))
export default store
collapseReducer
const collapseReducer = (prevState = false, action) => {
let { type, payload } = action
switch (type) {
case 'sideMenuShow':
return payload
default:
return prevState
}
}
export default collapseReducer
roleListReducer.js
const roleListReducer = (prevState =[], action) => {
let { type, payload } = action
switch (type) {
case 'setRoleList':
var newstate = { ...prevState }
newstate = payload
return newstate
default:
return prevState
}
}//只要狀態已返回,會自動更新
export default roleListReducer
React-Redux
同步寫法
app.js
import { Provider } from 'react-redux'
import store from './redux/store
<Provider store={store}>
<BlogRouter/>
</Provider >
釋出者
把方法對映成屬性用
第一個引數是商量好的那個屬性傳給孩子
第二個引數把方法對映成屬性用
import { connect } from 'react-redux'
const mapStateToprops=()=>{
return {
}
} //state 對映成屬性用
const mapDispathToProps={
actionCreator:(iscollapsed)=>{
return {
type: 'sideMenuShow',
payload: iscollapsed
}
}
}
export default withRouter(connect(mapStateToprops,mapDispathToProps)(TopHeader))
訂閱者接收
import { connect } from 'react-redux'
const mapStateTopprops=(state)=>{
return {
iscollapsed:state.iscollapsed
}//約定isCollapsed 屬性
}
export default withRouter(connect(mapStateTopprops)(SideMenu))
非同步
if (this.props.datalist.length == 0) {
//直接呼叫改方法 會把狀態傳遞給redux
this.props.setList()
}
//訂閱者 state 中可以獲取到redux中的狀態
const mapStateToprops = (state) => {
return {
datalist:state.rightList
}
} //state 對映成屬性用
//會自動傳遞給redux
const mapDispathToProps = {
setList : () => {
// 返回一個promise物件
return axios.get("http://localhost:8000/rights").then(res => {
// 自己決定什麼時候傳送
return {
type: 'setRightsList',
payload: res.data
}
})
}
}
// } //把方法對映成屬性用
export default connect(mapStateToprops,mapDispathToProps)(Right)
Redux和React-Redux關係
mobx
1、box方法
只能觀察 簡單資料型別
store.js
import { observable } from 'mobx'
const store = observable.box(true)
export default store
// 傳播者
import store from '../../mobx/store'
store.set(false)
// 接收者
import store from '../../mobx/store'
import { autorun } from 'mobx'
autorun(() => {
console.log(store.get())
})
2、map方法
觀察複雜資料型別
const store = observable.map({
isshow:true,
list:[],
roleList:[],
rightList:[]
})
store.set('isshow',false)
mobx 優點:
- mobox寫法上更偏向於oop
- mobox 對一份資料直接進行修改操作,不需要始終返回一個新的資料
- mobox 並非單一的 store。可以多 store
- redux 預設以 javaScript 原生物件形式儲存資料,而 mobx 可以用來觀察物件
mobx 缺點:
mobx提供的約定及模板程式碼很少,程式碼編寫很自由,如果不做一些約定,比較容易導致團隊程式碼風格不統一
相關中介軟體很少,邏輯層業務整合式問題
遇到的bug
第一個bug:
解決方案:
取消觀察
this.cancel = autorun(() => {
this.setState({
code: store.get('isshow')
}
//取消觀察
componentWillUnmount() {
this.cancel()//取消觀察
}
第二個bug:
解決方案:
網速很慢的時候資料沒有回來 ajax請求的資料沒有回來
componentWillUnmount() {
this.setState=()=>{}
console.log('列表銷燬','取消ajax')
}
React Hooks
Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。
useState寫法
import React,{useState}from 'react'
export default function App() {
const [name, setName] = useState('kerwin')//初始值[狀態,改變狀態的方法]
const [age, setAge] = useState('12')//初始值[狀態,改變狀態的方法]
return (
<div>
app-{name}-{age}
<button onClick={()=>{
setName('xiaoming')
setAge('18')
}}>click</button>
</div>
)
}
獲取ref
import React, { useState, useRef }from 'react'
const mytext = useRef(null)
<input type='text' onChange={(ev)=>{
settext(ev.target.value)
}} ref={mytext}/>
點選事件
<button onClick={() => handleDeleClick(index)}>dele</button>
const handleDeleClick=(index)=>{
console.log(index)
var newlist=[...list]
newlist.splice(index,1)
setlist(newlist)
}
替代複雜的生命週期(useEffect)
格式: useEffect(處理函式,[依賴])
如果依賴傳的是一個空陣列,相當於 componentWillMount , 掛載前,只執行一次
如果第二個引數不傳,代表任何狀態改變,都會重新執行
useEffect(()=>{
},[])
** 更新: [依賴] 只有依賴改變的時候 才會執行一次**
// age 更新會重新執行
useEffect(()=>{
console.log('建立或更新')
},[age])
建立/銷燬
useEffect(() => {
var id=setInterval(() => {
console.log(111)
}, 1000);
console.log('建立')
return () => {
// cleanu
clearInterval(id)
console.log('銷燬')
}
}, [])
獲取props
export default function Prebiew(props) {}
useCallback 提高執行效率
防止因為元件重新渲染,導致方法被重新建立,提高效能
const test=useCallback(
() => {
console.log(text)
},
[text]
)//閉包,快取函式,提高效能
test()
useReducer和useContext
在 hooks 中提供了的 useReducer 功能,可以增強 ReducerDemo 函式提供類似 Redux 的功能,引入 useReducer 後,useReducer 接受一個 reducer 函式作為引數,reducer 接受兩個引數一個是 state 另一個是 action 。然後返回一個狀態 count 和 dispath,count 是返回狀態中的值,而 dispatch 是一個可以釋出事件來更新 state 的
reducer.js
const reducer = (prevstate, action) => {
let { type, payload } = action
switch (type){
case "Change_text":
// 深複製
return {
...prevstate, text: payload
}
case "Change_list":
// 深複製
return {
...prevstate, list: payload
}
}
return prevstate
}
export default reducer
index.js (GlobalContext )
index.js (GlobalContext )
import React from 'react'
const GlobalContext = React.createContext()
export default GlobalContext
app.js
import GlobalContext from './store/index'
import reducer from './store/reducer'
import React ,{useReducer,useContext}from 'react'
const App=() =>{
// 表示reducer 傳入初始值 代表reducer管理這些狀態
const [state, dispatch] = useReducer(reducer, {
isShow: true,
list: [],
text: "我是公共的狀態"
}) //[公共的狀態,改變公共狀態的方法]
return <GlobalContext.Provider value={{
state,
dispatch
}}>
<Child1/>
</GlobalContext.Provider>
}
//獲取到app傳來的資訊 state可以直接獲取到 dispatch 可以進行修改狀態
const Child1=()=>{
let { state, dispatch } = useContext(GlobalContext) //不需要consumer
// console.log(useContext(GlobalContext))
return <div>
//同步:
child1-{state.text}<button onClick={()=>{
dispatch({type:'Change_text',
payload:'child1111111'
})
}}>click</button>
</div>
// 異端:
axios.get("http://localhost:8000/users").then(res=>{
console.log(res.data)
dispatch({
type: 'Change_list',
payload: res.data
})
})
}
自定義Hooks
** 當我們想在兩個函式之間共享邏輯時,可以把它提取到第三個函式中
必須以'use'開頭嗎?**
必須如此。這個約定非常重要,不遵循的話,由於無法判斷某個函式是否包含其內部 Hook 的呼叫,React 將無法自動檢測你的Hooks 是否違反了Hook的規則
//為preview 元件提供資料
const usePrebiewDate = (props)=>{
const [title, settitle] = useState('')
const [content, setcontent] = useState('')
const [category, setcategory] = useState([])
useEffect(() => {
axios.get(`http://localhost:8000/articles/${props.match.params.myid}`).then(res => {
console.log(res.data)
let { title, category, content } = res.data
settitle(title);
setcontent(content);
setcategory(category);
})
return () => {
}
}, [props])
return {
title,
content,
category
}
}
let { title, content, category}= usePrebiewDate(props)