# 3-4-Store

这节课我们来介绍redux当中最核心的部分Store.

* Store
  * getState
  * dispatch
  * subscribe
  * unsubscribe
* createStore
  * reducer
  * defaultState

Store是我们储存状态数据state的地方。我们通过redux当中的createStore方法来创建一个store

```javascript
import { createStore } from 'redux'
import counter from './reducers'
const store = createStore(counter)
```

我们先从redux库当中引入createStore方法，之后使用const关键字定义store，我们需要给createStore方法中传入我们之前定义好的reducer函数，这样就可以创建出来一个存储reducer方法处理的状态数据的store

那么createStore到底做了一些什么操作呢？我们可以先把生成的store对象输出来看一下，我们会发现它提供了3个主要的方法，另外一个replaceReducer现在还不需要看，我们基本上用不到它。

其中getState方法是用来获取当前存储中的状态数据。

dispatch则是具体将传入的action操作执行并触发listeners事件监听器的地方。

subscribe则是用来添加listener的方法，同时它会返回一个unsubscribe的方法用来取消listener。这里的listener说白了其实就是当状态数据改变时会自动触发的一些事件。

前面我们就介绍过了，redux是一个非常简单的库，在这里我们可以模拟一下createStore的源码，来加深同学们对store的理解：

```javascript
const createStore = (reducer) => {
  let state
  let listeners = []
  // 用来返回当前的state
  const getState = () => state
  // 根据action调用reducer返回新的state并触发listener
  const dispatch = (action) => {
      state = reducer(state, action)
      listeners.forEach(listener => listener())
    }
  /* 这里的subscribe有两个功能
   * 调用 subscribe(listener) 会使用listeners.push(listener)注册一个listener
   * 而调用 subscribe 的返回函数则会注销掉listener
   */
  const subscribe = (listener) => {
      listeners.push(listener)
      return () => {
        listeners = listeners.filter(l => l !== listener)
      }
    } 

  return { getState, dispatch, subscribe }
}
```

我们可以看到，createStore接受reducer作为参数，返回的是一个包含上述的三个方法的对象。其中getState就非常简单了，直接返回我们的状态数据；而dispatch则需要进行两个操作，一个是根据传入的action具体调用reducer来获取新的状态数据，之后还需要触发所有的listener，响应状态数据改变之后的动作；最后是subscribe，则是用来添加注册listener的，同时它还会返回一个去除注销listener的方法。虽然我们这里是在模拟实现redux，这也几乎就是我们要使用的redux当中createStore方法的全部功能了。

好，之前的课程中呢，我们都是在模拟redux，应用redux的理念写了一些原生的js方法，这节课呢，我们来看一下redux这个库本身的基本使用方法，下面我们来一起编写一个Redux使用的基本例子：

```javascript
const counter = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}
// 这里使用的是ES6的解构赋值的方法
const { createStore } = Redux
// import { createStore } from 'redux'

const store = createStore(counter)
console.log(store.getState())

/* document.addEventListener('click', () => {
  store.dispatch({ type: 'INCREMENT' })
}) */

const render = () => {
  document.body.innerText = store.getState()
}

store.subscribe(() => { console.log(store.getState()) })
let unsubscribe = store.subscribe(render)

render()

store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'DECREMENT' })

unsubscribe()
console.log('unsubscribed!')
```

我们还是实现一个最基本的计数器的功能，首先定义我们的reducer方法，我们使用const关键字声明一个名为counter的函数，传入的参数为state和action，注意到这里我们传入的state后面跟着一个赋值等于0，这里使用到的是ES6的新特性，默认参数，也就是说我们随后调用counter方法如果不传入state的话，它默认会传入0作为state的值。

之后我们写一个switch的逻辑结构，这里也相当于定了好了action的类型，如果是increment增加的话，我们就为state加上1，如果是decrement减少的话，就给state减去1，而如果传入的是我们应用没设计的其他动作，我们就不对state进行操作。

接下来我们从redux当中引入createStore方法，如果你和我一样是在浏览器中编写redux的代码的话，我们需要像之前引入react时一样，在js窗格的设置中引入redux库文件的链接，之后在我们的代码里用解构赋值的方法获取到redux库当中的createStore，这种赋值方法也是es6当中的新特性，可以让我们直接获取到目标对象当中的同名方法。

之后呢，就是调用createStore方法来定义好我们的sotre对象。

然后，我们编写一个render方法，好让我们的状态数据能够在页面当中显示出来。

我们使用store的subscribe方法，将render绑定到listener里面，这样每当状态数据改变时，render方法就会被自动触发，页面当中的显示就能随之更新。

```javascript
const counter = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}
// 这里使用的是ES6的解构赋值的方法
const { createStore } = Redux
// import { createStore } from 'redux'

const store = createStore(counter)
console.log(store.getState())

/* document.addEventListener('click', () => {
  store.dispatch({ type: 'INCREMENT' })
}) */

const render = () => {
  document.getElementById('root').innerText = store.getState()
}

store.subscribe(() => { console.log(store.getState()) })
let unsubscribe = store.subscribe(render)

render()

document.getElementById('increment').addEventListener('click',function() {
  store.dispatch({ type: 'INCREMENT' })
})

document.getElementById('decrement').addEventListener('click',function() {
  store.dispatch({ type: 'DECREMENT' })
})

document.getElementById('unsubscribe').addEventListener('click',function() {
  unsubscribe()
  console.log('unsubscribed!')
})
```

接下来，再为我们的示例添加一些可以交互的功能来改变状态数据，我们在这里添加了三个按钮，分别绑定increment方法、decrement方法以及unsubscribe方法。接下来我们来编写这三个方法。increment直接调用store的dispatch方法，并传入action.type类型为increment作为参数，decrement方法也是类似，传入decrement类型的action作为参数。而unsubscribe方法呢，我们在本节课开始的时候介绍了，store的subscribe方法返回的就是一个现成的unsubscribe方法，因此我们之间赋值刚才的绑定render的方法给unsubscribe就能够获取到它返回的函数。

接下来我们再点击这几个按钮实际操作一下。当我们的事件绑定函数被触发时，页面当中显示的状态数据也就自动更新了。

我们使用redux编写应用时，首要的就是定义一个具体的store对象，因为不管是action还是reducer都是通过store当中的方法具体被调用了，而获取store需要使用到createstore方法，这个方法需要传入reducer作为参数，所以我们在编写代码时需要先写出reducer方法。话又绕了回来，reducer方法需要判断action的类型，所以事实上，最先要被定义好的应该是各种类型的action，action又是用来传入修改state状态数据的参数的，所以最早定义好的应该是应用的state状态数据。

我们在开发稍具规模的一些应用时，肯定是要先规划好应用要处理的数据结构。使用redux时最好先在一个单独的文件中定义好基本的action类型，其实这也就相当于在设计我们的应用都有哪些功能，用户可以触发哪些功能，对数据会造成什么影响。接下来就是获取到store，给我们的用户界面上面添加响应用户操作的事件，以及事件触发之后状态数据改变，我们的界面又要如何跟着响应改变。这其实也就是使用redux进行开发的一般思路啦。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://discountry.gitbook.io/learn-react-from-scratch/redux/3-4-store.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
