What is the best way to access redux store outside

2019-01-05 09:51发布

@connect works great when I'm trying to access the store within a react component. But how should I access it in some other bit of code. For eg: let's say I want to use an authorization token for creating my axios instance that can be used globally in my app, what would be the best way to achieve that?

This is my 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

Now I want to access a data point from my store, here is what that would look like if I was trying to fetch it within a react component using @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() {...}
}

Any insights or workflow patterns out there?

7条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-01-05 10:28

Like @sanchit proposed middleware is a nice solution if you are already defining your axios instance globally.

You can create a middleware like:

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;

And use it like this:

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

It will set the token on every action but you could only listen for actions that change the token for example.

查看更多
虎瘦雄心在
3楼-- · 2019-01-05 10:33

Export the store from the module you called createStore with. Then you are assured it will both be created and will not pollute the global window space.

MyStore.js

const store = createStore(myReducer);
export store;

or

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

MyClient.js

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

or if you used default

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

For Multiple Store Use Cases

If you need multiple instances of a store, export a factory function. I would recommend making it async (returning a promise).

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

On the client (in an async block(

import {getUserStore} from './store'

const joeStore = await getUserStore('joe')
查看更多
不美不萌又怎样
4楼-- · 2019-01-05 10:34

Seems like Middleware is the way to go.
Refer the official documentation and this issue on their repo

查看更多
你好瞎i
5楼-- · 2019-01-05 10:34

This question is quite old, but I think it worth sharing my idea.

Rather than storing token in redux store, I store it in memory.
* When app loaded, read TOKEN from AsyncStorage (or somewhere else) and set it to set it to

import {updateToke} from 'path_to_the_below_module';

updateToken({your token}). 

Here is a snippet code I have done.

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;

Simply way to use it

import networkRequest from '...pathtothemodule';

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

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

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

查看更多
冷血范
6楼-- · 2019-01-05 10:35

For TypeScript 2.0 it would look like this:

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(...)}
查看更多
萌系小妹纸
7楼-- · 2019-01-05 10:40

Found a solution. So I import the store in my api util and subscribe to it there. And in that listener function I set the axios' global defaults with my newly fetched token.

This is what my new api.js looks like:

// 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

Maybe it can be further improved, cause currently it seems a bit inelegant. What I could do later is add a middleware to my store and set the token then and there.

查看更多
登录 后发表回答