react深入 - 手寫實現react-redux api
簡介:簡單實現react-redux基礎api
react-redux api回顧
<Provider store>
把store放在context裡,所有子元件可以直接拿到store資料
使元件層級中的 connect() 方法都能夠獲得 Redux store
根元件應該巢狀在 <Provider> 中
ReactDOM.render( <Provider store={store}> <MyRootComponent /> </Provider>, rootEl ) ReactDOM.render( <Provider store={store}> <Router history={history}> <Route path="/" component={App}> <Route path="foo" component={Foo}/> <Route path="bar" component={Bar}/> </Route> </Router> </Provider>, document.getElementById('root') )
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
連結元件和資料,把redux中的資料放到元件的屬性中
[mapStateToProps(state, [ownProps]): stateProps] (Function)
如果定義該引數,元件將會監聽 Redux store 的變化。任何時候,只要 Redux store 發生改變,mapStateToProps 函式就會被呼叫。該回調函式必須返回一個純物件,這個物件會與元件的 props 合併
如果你省略了這個引數,你的元件將不會監聽 Redux store
ownProps,則該引數的值為傳遞到元件的 props,而且只要元件接收到新的 props,mapStateToProps 也會被呼叫,被重新計算
mapStateToProps 函式的第一個引數是整個Redux store的state,它返回一個要作為 props 傳遞的物件。它通常被稱作 selector (選擇器)。 可以使用reselect去有效地組合選擇器和計算衍生資料.
注意:如果定義一個包含強制性引數函式(這個函式的長度為 1)時,ownProps 不會傳到 mapStateToProps
const mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
}
}
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function)
Object: 它的每個鍵名也是對應 UI 元件的同名引數,鍵值應該是一個函式(action creator),會被當作 Action creator ,返回的 Action 會由 Redux 自動發出, Function: 會得到dispatch和ownProps(容器元件的props物件)兩個引數(此時可能用到Redux 的輔助函式 bindActionCreators())
省略這個 mapDispatchToProps 引數,預設情況下,dispatch 會注入到你的元件 props 中,你可以this.props.dispatch呼叫
指定了該回調函式中第二個引數 ownProps,該引數的值為傳遞到元件的 props,而且只要元件接收到新 props,mapDispatchToProps 也會被呼叫
eg:
connect(mapStateToProps, {
hideAdPanel,
pushAdData,
})(AdPanel)
function mapDispatchToProps(dispatch) {
return {
todoActions: bindActionCreators(todoActionCreators, dispatch),
counterActions: bindActionCreators(counterActionCreators, dispatch)
}
}
知識點補充 - React高階元件(Higher-Order Components)
高階元件就是一個函式,且該函式接受一個元件作為引數,並返回一個新的元件
高階元件就是一個沒有副作用的純函式
使用場景:兩個元件大部分程式碼都是重複的+且更好的封閉性,不需要關注資料的獲取
import React, {Component} from 'react'
class Welcome extends Component {
constructor(props) {
super(props);
this.state = {
username: ''
}
}
componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
}
render() {
return (
<div>welcome {this.state.username}</div>
)
}
}
export default Welcome;
import React, {Component} from 'react'
class Goodbye extends Component {
constructor(props) {
super(props);
this.state = {
username: ''
}
}
componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
}
render() {
return (
<div>goodbye {this.state.username}</div>
)
}
}
export default Goodbye;
welcome和goodbye元件相似,只能獲取的資料不一樣,用高階元件,提取公共部分
import React, {Component} from 'react'
export default (WrappedComponent) => {
class NewComponent extends Component {
constructor() {
super();
this.state = {
username: ''
}
}
componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
}
render() {
return <WrappedComponent username={this.state.username}/>
}
}
return NewComponent
}
簡化welcome和goodbye
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';
class Welcome extends Component {
render() {
return (
<div>welcome {this.props.username}</div>
)
}
}
Welcome = wrapWithUsername(Welcome);
export default Welcome;
此時,理解react-redux 的connect就好理解了
ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component);
// connect是一個返回函式的函式(就是個高階函式)
const enhance = connect(mapStateToProps, mapDispatchToProps);
// 返回的函式就是一個高階元件,該高階元件返回一個與Redux store
// 關聯起來的新元件
const ConnectedComment = enhance(Component);
provider實現
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose} from 'redux'
import thunk from 'redux-thunk'
import { counter } from './index.redux'
// import { Provider } from 'react-redux'
// 換成自己的Provider實現
import { Provider } from './self-react-redux'
import App from './App'
const store = createStore(counter, compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
))
ReactDOM.render(
(
<Provider store={store}>
<App />
</Provider>
),
document.getElementById('root'))
./self-react-redux
import React from 'react'
import PropTypes from 'prop-types'
export function connect(){
}
class Provider extends React.Component{
static childContextTypes = {
store: PropTypes.object
}
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render(){
return this.props.children
}
}
connect實現
demo
import React from 'react'
// import { connect } from 'react-redux'
import { connect } from './self-react-redux'
import { addGun, removeGun, addGunAsync } from './index.redux'
@connect(
// 你要state什麼屬性放到props裡
state=>({num:state.counter}),
// 你要什麼方法,放到props裡,自動dispatch
{ addGun, removeGun, addGunAsync }
)
class App extends React.Component{
render(){
return (
<div>
<h1>現在有機槍{this.props.num}把</h1>
<button onClick={this.props.addGun}>申請武器</button>
<button onClick={this.props.removeGun}>上交武器</button>
<button onClick={this.props.addGunAsync}>拖兩天再給</button>
</div>
)
}
}
export default App
./self-react-redux.js
// 高階元件的寫法
export function connect(maoStateToProps, mapStateToProps) {
return function(WrapComponent) {
return class ConnectComponent extends React.Component{
}
}
}
import React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreator } from './self-redux'
// 使用簡寫形式
// connect負責連結元件,給到redux裡的資料放在元件的屬性裡
// 1. 負責接收一個元件,把state裡的一些資料放進去,返回一個元件
// 2. 資料變化的時候,能通知元件
export const connect = (
mapStateToProps = state => state,
mapDispatchToProps ={}
) => (WrapComponent) => {
return class ConnectComponent extends React.Component {
static contextTypes = {
store: PropTypes.object
}
constructor(props, context){
super(props, context)
this.state = {
props: {}
}
}
// 2 實現了mapStateToProps
componentDidMount() {
const { store } = this.context
store.subscribe(() => this.update())
this.update()
}
update() {
const { store } = this.context
// store.getState()這就是為什麼mapStateToProps函式裡面能拿到state
const stateProps = mapStateToProps(store.getState())
// 方法不能直接給,因為需要dispatch
/**
function addGun() {
return { type: ADD_GUN }
}
直接執行addGun() 毫無意義
要 addGun = () => store.dispatch(addGun()) 才有意義,其實就是把actionCreator包了一層
bindActionCreators在手寫redux api實現了
*/
const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch)
// 注意state的順序問題會覆蓋
this.setState({
props: {
...this.state.props,
...stateProps,
...dispatchProps,
}
})
}
// 1
render() {
return <WrapComponent {...this.state.props}></WrapComponent>
}
}
}
./self-redux.js
// creators: {addGun, removeGun, addGunAsync}
// creators[v]:addGun(引數)
// 返回:(引數) => dispatch(addGun(引數))
function bindActionCreator(creator, dispatch) {
return (...args) => dispatch(creator(...args))
}
export function bindActionCreators(creators, dispatch) {
let bound = {}
Object.keys(creators).forEach( v => {
let creator = creators[v]
bound[v] = bindActionCreator(creator, dispatch)
})
return bound
}
// 簡寫
export function bindActionCreators(creators, dispatch) {
return Object.keys(creators).reduce((ret, item) => {
ret[item] = bindActionCreator(creators[item], dispatch)
return ret
}, {})
}
原文地址:https://segmentfault.com/a/1190000016759675