首页 文章

在反应组件外部访问redux存储的最佳方法是什么?

提问于
浏览
118

当我想要使用授权令牌创建可在我的应用程序中全局使用的axios实例时, @connect 效果很好,实现这一目标的最佳方法是什么?

这是我 api.js

// tooling modules
import axios from 'axios'

// configuration
const api = axios.create()
api.defaults.baseURL = 'http://localhost:5001/api/v1'
api.defaults.headers.common['Authorization'] = 'AUTH_TOKEN' // need the token here
api.defaults.headers.post['Content-Type'] = 'application/json'

export default api

现在我想从我的商店访问一个数据点,如果我试图使用 @connect 在反应组件中获取它,那么这就是我的样子

// connect to store
@connect((store) => {
  return {
    auth: store.auth
  }
})
export default class App extends Component {
  componentWillMount() {
    // this is how I would get it in my react component
    console.log(this.props.auth.tokens.authorization_token) 
  }
  render() {...}
}

那里有任何见解或工作流程模式吗?

7 回答

  • 12

    从您调用createStore的模块导出存储 . 然后你可以肯定它将被创建并且不会污染全局窗口空间 .

    MyStore.js

    const store = createStore(myReducer);
    export store;
    

    要么

    const store = createStore(myReducer);
    export default store;
    

    MyClient.js

    import {store} from './MyStore'
    store.dispatch(...)
    

    或者如果您使用默认值

    import store from './MyStore'
    store.dispatch(...)
    

    对于多个商店用例

    如果需要多个商店实例,请导出工厂函数 . 我建议把它变成 async (返回 promise ) .

    async function getUserStore (userId) {
       // check if user store exists and return or create it.
    }
    export getUserStore
    

    在客户端上(在 async 块中(

    import {getUserStore} from './store'
    
    const joeStore = await getUserStore('joe')
    
  • 8

    找到了解决方案 . 所以我在我的api util中导入商店并在那里订阅它 . 在该侦听器功能中,我使用新获取的令牌设置axios的全局默认值 .

    这是我的新 api.js 看起来像:

    // tooling modules
    import axios from 'axios'
    
    // store
    import store from '../store'
    store.subscribe(listener)
    
    function select(state) {
      return state.auth.tokens.authentication_token
    }
    
    function listener() {
      let token = select(store.getState())
      axios.defaults.headers.common['Authorization'] = token;
    }
    
    // configuration
    const api = axios.create({
      baseURL: 'http://localhost:5001/api/v1',
      headers: {
        'Content-Type': 'application/json',
      }
    })
    
    export default api
    

    也许它可以进一步改进,因为目前它似乎有点不优雅 . 我以后可以做的是在我的商店中添加一个中间件,然后在那里设置令牌 .

  • 3

    似乎是 Middleware 是要走的路 .
    在他们的回购中提及the official documentationthis issue

  • 0

    就像@sanchit提出的中间件是一个很好的解决方案 if 你已经在全球范围内定义了你的axios实例 .

    您可以创建一个中间件,如:

    function createAxiosAuthMiddleware() {
      return ({ getState }) => next => (action) => {
        const { token } = getState().authentication;
        global.axios.defaults.headers.common.Authorization = token ? `Bearer ${token}` : null;
    
        return next(action);
      };
    }
    
    const axiosAuth = createAxiosAuthMiddleware();
    
    export default axiosAuth;
    

    并像这样使用它:

    import { createStore, applyMiddleware } from 'redux';
    const store = createStore(reducer, applyMiddleware(axiosAuth))
    

    它将在每个操作上设置令牌,但您只能侦听更改令牌的操作 .

  • 99

    这个问题很老了,但我认为值得分享我的想法 .

    我将它存储在内存中,而不是将令牌存储在redux存储中 .
    *加载应用程序时,从AsyncStorage(或其他地方)读取TOKEN并将其设置为将其设置为

    import {updateToke} from 'path_to_the_below_module';
    
    updateToken({your token}).
    

    这是我做过的代码片段 .

    import Axios from "axios";
    import { AsyncStorage } from 'react-native';
    
    const AUTH_TOKEN='AUTH_TOKEN';
    const BASE_URL = 'http://localhost:5001/api/v1';
    let authenticationToken = {};
    
    export const updateToken = (token) => {
        AsyncStorage.setItem(AUTH_TOKEN, JSON.stringify(token));
        authenticationToken = token;
    };
    
    const networkRequest = Axios.create({
        baseURL: BASE_URL,
    });
    
    networkRequest.interceptors.request.use(config => {
        const bearer = `Bearer ${authenticationToken.access_token}`;
        if (bearer) {
            config.headers['Authorization'] = bearer;
        }
        console.log(config);
        return config;
    }, error => Promise.reject(error));
    
    export default networkRequest;
    

    简单地使用它

    import networkRequest from '...pathtothemodule';
    
    networkRequest.get('/user/info').then(....).catch(...);
    

    Note that every time you refresh token, make sure to call updateToken() in order to keep it the latest.

    This is another alternative I used in my project, and I wish go get your ideas.

  • 27

    您可以使用从 createStore 函数返回的 store 对象(应在应用程序初始化中的代码中使用该对象) . 您可以使用此对象通过 store.getState() 方法获取当前状态或使用 store.subscribe(listener) 订阅商店更新 .

    您甚至可以将此对象保存到 window 属性,以便从应用程序的任何部分访问它(如果您真的需要它)( window.store = store

    更多信息可以在Redux documentation找到 .

  • 4

    对于TypeScript 2.0,它看起来像这样:

    MyStore.ts

    export namespace Store {
    
        export type Login = { isLoggedIn: boolean }
    
        export type All = {
            login: Login
        }
    }
    
    import { reducers } from '../Reducers'
    import * as Redux from 'redux'
    
    const reduxStore: Redux.Store<Store.All> = Redux.createStore(reducers)
    
    export default reduxStore;
    

    MyClient.tsx

    import reduxStore from "../Store";
    {reduxStore.dispatch(...)}
    

相关问题