將 appState
以及 stateChanger
合併
stateChanger 若沒有傳入 state,會初始化數據。
function stateChanger (state, action) {
if (!state) {
return {
title: {
text: 'React.js 小書',
color: 'red',
},
content: {
text: 'React.js 小書內容',
color: 'blue'
}
}
}
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'UPDATE_TITLE_COLOR':
return {
...state,
title: {
...state.title,
color: action.color
}
}
default:
return state
}
}
優化 createStore
成一個參數,初始化 state
因為 state 和 stateChanger 合併到一起。
createStore
不再通過參數傳入,而是一個區域變數 let state =null
createStore
內部第一次的 dispatch
導致 state
初始化完成,後續的 dispatch
就是修改數據的行為。
function createStore (stateChanger) {
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = stateChanger(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}
總結
這邊實作出來的 stateChanger
就是 reducer
reducer
就是修改 createStore
參數的名字
reducer
不允許有副作用,不能在裡面操作 DOM、不能發 Ajax 請求,更不能直接修改 state
。
reducer
僅初始化以及計算新的 state
。
createStore
createStore
,接受一個叫 reducer
的函數作為參數,只接收 state
、action
。
如果沒有傳入 state
或是 state
是 null
,就會回傳一個初始化的數據。
如果有傳入 state
,便可根據 action
來"修改"數據,但其實它沒有修改數據、也不能修改數據,而是透過修改記憶體位置。若不能識別 action
,就不會產生新的數據,而把 state
原封不動回傳。
本節完整程式碼
let appState = {
title: {
text: 'React.js 小書',
color: 'red',
},
content: {
text: 'React.js 小書內容',
color: 'blue'
}
}
let newAppState = {
...appState, // 複製 appState 裡面的內容
title: { // 用一個新的物件覆蓋原來 title 屬性
...appState.title, // 複製原來 title 裡面的內容
text: '新的 React.js 小書' // 覆蓋 text 屬性
}
}
let newAppState1 = {
...newAppState, // 複製 newAppState 裡面的內容
title: { // 用一個新的物件覆蓋原來 title 屬性
...newAppState.title, // 複製原來 title 裡面的內容
text: '新的 React.js 小書' // 覆蓋 color 屬性
}
}
console.log('1. ', newAppState !== newAppState1) // ture
console.log('2. ', appState.title !== newAppState.title) // ture
console.log('3. ', appState.content !== appState.content) // false
// 新增渲染函數
function renderApp (newAppState, oldAppState = {}) { // 防止 oldAppState 没有傳入,所以加了默認參數 oldAppState = {}
if (newAppState === oldAppState) return
console.log('render app...')
renderTitle(newAppState.title, oldAppState.title)
renderContent(newAppState.content, oldAppState.content)
}
function renderTitle (newTitle, oldTitle) {
if (newTitle === oldTitle) return
console.log('render title')
const titleDOM = document.getElementById('title')
titleDOM.innerHTML = newTitle.text
titleDOM.style.color = newTitle.color
}
function renderContent (newContent, oldContent) {
if (newContent === oldContent) return
console.log('render content')
const contentDOM = document.getElementById('content')
contentDOM.innerHTML = newContent.text
contentDOM.style.color = newContent.color
}
function stateChanger (state, action) {
if (!state) {
return {
title: {
text: 'React.js 小書',
color: 'red',
},
content: {
text: 'React.js 小書內容',
color: 'blue'
}
}
}
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'UPDATE_TITLE_COLOR':
return {
...state,
title: {
...state.title,
color: action.color
}
}
default:
return state // 沒有修改,回傳原來的狀態
}
}
// createStore
function createStore (reducer) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = reducer(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}
function themeReducer (state, action) {
if (!state) return {
themeName: 'Red Theme',
themeColor: 'red'
}
switch (action.type) {
case 'UPATE_THEME_NAME':
return { ...state, themeName: action.themeName }
case 'UPATE_THEME_COLOR':
return { ...state, themeColor: action.themeColor }
default:
return state
}
}
const store = createStore(themeReducer)
// const store = createStore(appState, stateChanger)
let oldState = store.getState() // 儲存舊的 state
store.subscribe(() => {
const newState = store.getState() // 數據可能變化,獲取新的 state
renderApp(newState, oldState) // 把新舊的 state 傳進去渲染
oldState = newState // 渲染完成後,把新的 newState 變成舊的 oldState,等待下一次重新渲染
}) // 監聽數據變化
renderApp(store.getState()) // first render
store.dispatch({type: 'UPDATE_TITLE_TEXT', text: 'React 小書'}) // 修改內容
store.dispatch({type: 'UPDATE_TITLE_COLOR', color: 'blue'}) // 修改標題顏色
// renderApp(store.getState()) // redner again