web-dev-qa-db-ja.com

反応コンポーネントの外側で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() {...}
}

洞察やワークフローのパターンはありますか?

146
Subodh Pareek

CreateStoreを呼び出したモジュールからストアをエクスポートします。そうすれば、それは両方とも作成され、グローバルウィンドウスペースを汚染しないでしょう。

MyStore.js

const store = createStore(myReducer);
export store;

または

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

MyClient.js

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

または default を使用した場合

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')
116
Steven Spungin

解決策を見つけました。だから私は私の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

多分それはさらに改善することができます、今のところそれは少し洗練されていないようです。後でできることは、私の店にミドルウェアを追加して、その場でトークンを設定することです。

40
Subodh Pareek

store関数から返されるcreateStoreオブジェクトを使用できます(これは、アプリの初期化のコードで既に使用されているはずです)。このオブジェクトを使用してstore.getState()メソッドまたはstore.subscribe(listener)メソッドで現在の状態を取得し、ストアの更新を購読することができます。

本当に必要な場合は、このオブジェクトをwindowプロパティに保存して、アプリケーションの任意の部分からアクセスすることもできます(window.store = store

より詳しい情報は Reduxドキュメント にあります。

13
trashgenerator

Middlewareのような方法があります。
公式文書 および この問題 を参照してください。

9
sanchit

@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
erksch

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(...)}
4
Ogglas

この質問はかなり古いものですが、私の考えを共有する価値があると思います。

トークンを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(...);

トークンを更新するたびに、それを最新に保つためにupdateToken({your token})を呼び出すようにしてください。

これは私が私のプロジェクトで使ったもう一つの選択肢です、そして私はあなたの考えを取りに行きたいです。

0
Leu

トークンにアクセスする簡単な方法は、トークンをLocalStorageまたはReact Native付きの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 };

Webの場合は、AsyncStorageの代わりにWindow.localStorageを使用できます。

0
Denis Zheng