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

时间:2016-07-19 14:09:55

标签: reactjs redux react-redux

当我尝试在反应组件中访问商店时,

@connect效果很好。但是我应该如何在其他一些代码中访问它。例如:假设我想使用授权令牌来创建可在我的应用程序中全局使用的axios实例,那么实现该目标的最佳方法是什么?

这是我的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() {...}
}

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

10 个答案:

答案 0 :(得分:117)

从您调用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')

答案 1 :(得分:39)

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

这是我的新filesNames.map(fileName => { return fs.stat(fileName).then(stat => { return {name: fileName, stat: stat}; }); }); 的样子:

api.js

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

答案 2 :(得分:13)

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

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

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

答案 3 :(得分:10)

似乎Middleware是可行的方式 在他们的回购中提及the official documentationthis issue

答案 4 :(得分:5)

就像@sanchit建议的中间件一样,如果您已经在全局定义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))

它将在每个操作上设置令牌,但是您只能监听例如更改令牌的操作。

答案 5 :(得分: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(...)}

答案 6 :(得分:4)

可能有点晚了,但是我认为最好的方法是使用axios.interceptors,如下所示。导入网址可能会根据您的项目设置而更改。

index.js

import axios from 'axios';
import setupAxios from './redux/setupAxios';
import store from './redux/store';

// some other codes

setupAxios(axios, store);

setupAxios.js

export default function setupAxios(axios, store) {
    axios.interceptors.request.use(
        (config) => {
            const {
                auth: { tokens: { authorization_token } },
            } = store.getState();

            if (authorization_token) {
                config.headers.Authorization = `Bearer ${authorization_token}`;
            }

            return config;
        },
       (err) => Promise.reject(err)
    );
}

答案 7 :(得分:1)

用钩子做。 我遇到了类似的问题,但是我在用带钩子的react-redux。我不想用很多专门用于从商店检索信息/向商店发送信息的代码来充实我的界面代码(即,对组件进行反应)。相反,我希望使用具有通用名称的函数来检索和更新数据。我的路径是放置应用程序的

const store = createSore(
   allReducers,
   window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
 );

进入名为store.js的模块,并在export之前添加const,并在store.js中添加通常的react-redux导入。文件。然后,我在应用程序级别导入到index.js,然后与通常的import {store} from "./store.js"一起导入index.js。子组件随后使用useSelector()和{{1}访问商店}钩子。

要使用非组件前端代码访问商店,我使用了类似的导入方式(即useDispatch()),然后使用了import {store} from "../../store.js"store.getState()来处理检索和更新(er ,向商店发送操作。

答案 8 :(得分:1)

导出我的商店变量

<块引用>

export const store = createStore(rootReducer, applyMiddleware(ReduxThunk));

在操作文件或您的文件中需要它们导入(存储)

<块引用>

从“./path...”导入{store};

这一步用函数从存储变量中获取状态

<块引用>

const state = store.getState();

并获取您的应用的所有状态

答案 9 :(得分:0)

访问令牌的一种简单方法是将令牌放入带有React Native的LocalStorage或AsyncStorage中。

下面是带有 React Native 项目

的示例

authReducer.js

import { AsyncStorage } from 'react-native';
...
const auth = (state = initialState, action) => {
  switch (action.type) {
    case SUCCESS_LOGIN:
      AsyncStorage.setItem('token', action.payload.token);
      return {
        ...state,
        ...action.payload,
      };
    case REQUEST_LOGOUT:
      AsyncStorage.removeItem('token');
      return {};
    default:
      return state;
  }
};
...

api.js

import axios from 'axios';
import { AsyncStorage } from 'react-native';

const defaultHeaders = {
  'Content-Type': 'application/json',
};

const config = {
  ...
};

const request = axios.create(config);

const protectedRequest = options => {
  return AsyncStorage.getItem('token').then(token => {
    if (token) {
      return request({
        headers: {
          ...defaultHeaders,
          Authorization: `Bearer ${token}`,
        },
        ...options,
      });
    }
    return new Error('NO_TOKEN_SET');
  });
};

export { request, protectedRequest };

对于网络,您可以使用Window.localStorage代替AsyncStorage