Last active
February 15, 2020 05:09
-
-
Save hacker0limbo/b20148a7e59c0a012c6aaf87271b5b93 to your computer and use it in GitHub Desktop.
react redux 模拟实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const name = 'react-redux' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const createDOMFromString = (html) => { | |
const div = document.createElement('div') | |
div.insertAdjacentHTML('beforeend', html) | |
return div | |
} | |
const mount = (component, root) => { | |
root.insertAdjacentElement('beforeend', component.renderDOM()) | |
component.onStateChange = (oldEl, newEl) => { | |
root.insertAdjacentElement('beforeend', newEl) | |
root.removeChild(oldEl) | |
} | |
} | |
class Component { | |
constructor(props={}) { | |
this.props = props | |
this.el = null | |
this.state = {} | |
} | |
onStateChange(oldEl, newEl) { | |
console.log('state changed') | |
} | |
setState(newState) { | |
const oldEl = this.el | |
this.state = newState | |
this.el = this.renderDOM() | |
this.onStateChange(oldEl, this.el) | |
} | |
handleClick() { | |
console.log('clicked') | |
} | |
render() { | |
return '<div></div>' | |
} | |
renderDOM() { | |
this.el = createDOMFromString(this.render()) | |
this.el.addEventListener('click', e => this.handleClick(e), false) | |
return this.el | |
} | |
} | |
class LikeButton extends Component { | |
constructor(props) { | |
super(props) | |
this.state = { | |
liked: false | |
} | |
} | |
handleClick() { | |
this.setState({ | |
liked: !this.state.liked | |
}) | |
} | |
render() { | |
return ` | |
<div> | |
<button style="color: ${this.props.fontColor}"> | |
${this.state.liked ? 'Unlike it' : 'Like it'} | |
</button> | |
<span>${this.state.liked ? '👍' : '👎'}</span> | |
</div> | |
` | |
} | |
} | |
const root = document.querySelector('#root') | |
const likeButton = new LikeButton({ fontColor: 'red' }) | |
mount(likeButton, root) | |
/** | |
* 1, 一旦状态发生改变,就重新调用 render 方法(在 setState 里面),构建一个新的 DOM 元素 | |
* 2, 每当 setState 中构造完新的 DOM 元素以后,就会通过 onStateChange 告知外部插入新的 DOM 元素,然后删除旧的元素,页面就更新了 | |
* 3, 调用过程: | |
* 1, 用户触发 handleClick 监听器 | |
* 2, 调用 setState, 重新设置 state, 并触发 renderDom, 根据 render 返回的 html, 重新渲染视图里有关状态的数据, 并重新绑定监听器 | |
* 3, 调用 onStateChange, 根据 render 之后返回的最新元素插入到页面, 这里 onStateChange 可以自定义 | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const createStore = (reducer, state) => { | |
const listeners = [] | |
const subscribe = listener => listeners.push(listener) | |
const getState = () => state | |
const dispatch = action => { | |
state = reducer(state, action) | |
listeners.forEach(listener => listener()) | |
} | |
// state 可选, 如果不提供(为 undefined), 那么可以在声明 reducer 的时候提供 | |
// 此时 dispatch 一个空的 action, 在 store 里面初始化 state | |
if (!state) { | |
dispatch({}) | |
} | |
return { getState, dispatch, subscribe } | |
} | |
const combineReducers = reducers => { | |
// reducers 是一个对象: { r1, r2 } | |
// 将所有的 reducers 集合起来, 对各个 reducer 里面的状态进行集合, 整合成一个新的 state | |
return (state={}, action) => { | |
return Object.keys(reducers).reduce((nextState, key) => { | |
nextState[key] = reducers[key](state[key], action) | |
return nextState | |
}, {}) | |
} | |
} | |
const counter = (state={ count: 0 }, action) => { | |
switch(action.type) { | |
case 'INCREMENT': | |
return { | |
...state, | |
count: state.count += action.count | |
} | |
case 'DECREMENT': | |
return { | |
...state, | |
count: state.count -= action.count | |
} | |
default: | |
return state | |
} | |
} | |
const book = (state=['React'], action) => { | |
switch(action.type) { | |
case 'ADD_BOOK': | |
return [ | |
...state, | |
action.newBook | |
] | |
case 'UPDATE_BOOK': | |
return state.map((book, index) => { | |
if (index !== action.index) { | |
return book | |
} | |
return action.newBook | |
}) | |
case 'DELETE_BOOK': | |
return state.filter((book, index) => index !== action.index) | |
default: | |
return state | |
} | |
} | |
const rootReducer = (combineReducers({ | |
counter, | |
book | |
})) | |
const store = createStore(rootReducer) | |
store.subscribe(() => console.log(store.getState())) | |
store.dispatch({ type: 'INCREMENT', count: 3}) | |
store.dispatch({ type: 'DECREMENT', count: 1}) | |
store.dispatch({ type: 'ADD_BOOK', newBook: 'Angular' }) | |
store.dispatch({ type: 'DELETE_BOOK', index: 1 }) | |
store.dispatch({ type: 'UPDATE_BOOK', newBook: 'Redux', index: 0 }) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment