这应该是最简单省事同时也容易想到的方法,但是有一点需要注意:
不要在服务端渲染中使用该方法
如果你在使用服务端渲染的 app 中直接导出 store,那么 app 的所有 user 都会获得一个单例的 store (包括相同的 JWT Token),这肯定不是你乐见的。
除去上述这种情况,当我们需要获取 store 中的 JWT Token 时,可以这么做:
/*
store.js
*/
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
export default store
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
创建 store 的方法还和以前一样,仅仅是导出它,以便于在其他文件中使用。
不要担心你使用的 createStore 方法可能会很复杂,放心的使用各种中间件比如 thunk, saga, devtools ,你需要做的仅仅是导出 store 而已。
紧接着,在需要用到 store 中数据的地方,引入 (import) 它。下面的例子就是我们在一个普通函数 api.js 中将 store 中的 JWT Token 通过 ajax 传递给服务端。
/**
api.js
**/
import store from './store'
export function getProtectedThing() {
// grab current state
const state = store.getState()
// get the JWT token out of it
// (obviously depends on how your store is structured)
const authToken = state.currentUser.token
// Pass the token to the server
return fetch('/user/thing', {
method: 'GET',
headers: {
Authorization: `Bearer ${authToken}`
}
}).then(res =>res.json())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在 Redux 官网的 FAQ 中有这样一个问题 我可以在组件中直接引入 store 并使用吗 ?
其实如果需要在 React 组件中使用 store,我们有更好的选择 (译者注:也是官方提倡的):那就是使用 React-Redux 中的 connect 方法。
Mark Erikson,Redux 的维护者之一,在 Reddit 上就这个问题发表过如下看法:
通常情况下,不建议在 component 中通过 import 的方式来使用 store.
在某些情况下,可以试着通过 thunk 或者其他一些 Redux 中间件来操作 store 而不是直接去引用它。
但是凡事总有例外,打我自己的比方,有一个项目中 70% 的代码使用 Backbone 写的,在项目的一些模块中,我们同时需要来自 Backbone models 和 Redux store 中的数据,在这部分模块中,我们的确需要 import {store} from “store” 这种直接的引用方式,因为我们的确没有其他选择。
所以,直接引用并不是理想的方式,但是,如果有必要,也可以。
React Component 之外 dispatch action
如果你在 React Component 之外需要 dispatch action,那么同样的方法也是适用的:引入 store 然后再调用 store.dispatch(),这和你在 react-redux 中通过 connect 函数获取到 dispatch 是一样的。
方法二:从 thunk 中获取 redux 的 state
如果你需要在 thunk 中获取 redux store 中的某项数据,更简单不过。你根本不需要直接引入 store,因为 thunk 的 action 中有一个 getState 的输入参数。
下面的例子就是从 thunk 的 action 创建函数中获取 JWT Token.
/** actions.js **/
export function getProtectedThing() {
return (dispatch, getState) =>{
// grab current state
const state = getState()
// get the JWT token out of it
// (obviously depends on how your store is structured)
const authToken = state.currentUser.token
// Pass the token to the server
return fetch('/user/thing', {
method: 'GET',
headers: {
Authorization: `Bearer ${authToken}`
}
}).then(res =>res.json())
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
另外,如果你不想在 thunk 的 action 函数中直接使用 fetch,你可以选择将 fetch 封装到另外一个类中比如 api.js. 当然你需要额外的参数将所需数据传递出去。
方法三:使用中间件(middleware)截获 action 中的数据
如果你不喜欢上述两种方法 (或是无法使用),那么也可以试试下面这种方法。
开发一个自定义的 redux 中间件,“拦截” 特定的 action. 在 action 触达 store 之前获取其携带的数据,甚至在中间件中直接获取 store.
我们还以 JWT Token 为例。假设应用程序在用户成功登陆后会触发一个 LOG_SUCCESS 的 action,该 action 携带的数据包含 JWT Token.
/*
index.js
*/
const saveAuthToken = store =>next =>action =>{
if(action.type === 'LOGIN_SUCCESS') {
// after a successful login, update the token in the API
api.setToken(action.payload.authToken)
}
// continue processing this action
return next(action)
}
const store = createStore(
reducer,
applyMiddleware(saveAuthToken)
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在你的 api.js 中会有一个 setToken 的方法将 token 赋值给本地变量。
/*
api.js
*/
let currentAuthToken = null
export function setToken(token) {
currentAuthToken = token
}
export function getProtectedThing() {
// Pass the token to the server
return fetch('/user/thing', {
method: 'GET',
headers: {
Authorization: `Bearer ${currentAuthToken}`
}
}).then(res =>res.json())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
如果你使用的 http 库是 axios, 你在 setToken 中可以直接将 token 赋值到 axios 实例的 Authorization header,axios 在发送请求时请求头会自动包含该字段。
import axios from 'axios'
export function setToken(token) {
axios.defaults.headers.common['Authorization'] =
`Bearer ${token}`
}
1
2
3
4
5
6
1
2
3
4
5
6
方法四:在 React 组件中传值
(译者注:这种方法和主题无关了,就是组件内通过 connect 的方式)
import React from 'react'
import { connect } from 'react-redux'
import * as api from 'api'
const ItemList = ({ authToken, items }) =>{
return (
<ul>
{items.map(item =>(
<li key={item.id}>
{item.name}
<button
onClick={
() =>api.deleteItem(item, authToken)
}>
DELETE THIS ITEM
</button>
</li>
)}
</ul>
)
}
const mapStateToProps = state =>({
authToken: state.currentUser &&state.currentUser.authToken,
items: state.items
})
export connect(mapStateToProps)(ItemList)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
状态管理有5个核心,分别是state、mutation、getter、action、module
(1)state
简单的理解,可以把state想象成组件中的data,专门用来存储数据的。
如果在组件中,想要访问store中的数据,只能通过```this.$store.state.xxx来访问
(2)mutation
负责修改state中的数据
如果要操作store中的state值,不推荐直接操作state中的数据,推荐通过调用mutations提供的方法来操作对应的数据。mutation的作用如下图:
(4)action
action和mutation有些类似,不同在于
(5)mudule
module解决了当state复杂臃肿的时候,module可以将store分割成模块,每个模块拥有直接的state、mutation、action和getter。