反応コンポーネントの外でreduxストアにアクセスする最良の方法は何ですか?


192

@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

次に、ストアからデータポイントにアクセスしたいと思います。次のコマンドを使用して、reactコンポーネント内でデータポイントをフェッチしようとすると、このようになります。 @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() {...}
}

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


あなたはAxiosインスタンスにreduxミドルウェアに住んで欲しくないのですか?この方法ですべてのアプリケーションで利用できるようになります
Emrys Myrooin

クラスapi内にインポートしAppて、認証トークンを取得した後に実行できますapi.defaults.headers.common['Authorization'] = this.props.auth.tokens.authorization_token;。同時に、それをlocalStorageにも保存できるため、ユーザーがページを更新すると、トークンがlocalStorageに存在するかどうか、およびトークンが存在するかどうかを確認できますそれを設定できます。取得したらすぐに、apiモジュールにトークンを設定するのが最善だと思います。
Dhruv Kumar Jha

1
Dan Abromovがここの問題キューの例を提供します:github.com/reactjs/redux/issues/916
chrisjlee

1
特定のレデューサーから特定の状態にアクセスする必要があるだけの場合は、どこからでも最新の状態にアクセスできるredux-named-reducersを試すことができます。
miles_christian 2018

回答:


146

呼び出したモジュールからストアをエクスポート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')

47
同形のアプリの警告:このサーバー側を実行すると、すべてのユーザー間でreduxストアが共有されます!!!
Lawrence Wagerfield 2017

6
この質問は、「ブラウザ」についても明示的に述べていません。ReduxとJavaScriptはどちらもサーバーとブラウザのどちらでも使用できるため、具体的に指定する方がはるかに安全です。
Lawrence Wagerfield 2017

7
ストアをエクスポートすると、サイクリックインポートの悪夢が生じるようです。createStoreには、アクション(少なくともアクションタイプの列挙型とアクションインターフェイス)を必要とするレデューサーが含まれています。あなたの減速やアクションのいずれかでストアを使用する(または環状輸入の周りの他の方法を口論。)してはならないので
Eloff

6
これが正しい答えですが、(私と同じように)ストアでアクションをディスパッチするのではなく読みたい場合は、電話をかける必要がありますstore.getState()
JuanJoséRamírezSep

3
さて、「access redux store」に関する私の解釈は、その店を「読んだ」というものでした。他人を助けようとしているだけです。
ファンホセラミレス

47

解決策を見つけました。そのため、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

多分それはさらに改善することができます、現在それは少しエレガントではないように見えるので。後でできることは、ミドルウェアをストアに追加して、その場でトークンを設定することです。


1
store.jsファイルがどのように見えるかを共有できますか?
2016年

まさに私が探していたもの、おかげでトン@subodh
Harkirat Saluja

1
質問が古いことはわかっていますが、自分の答えを正解として受け入れることができます。これにより、最終的にここに来る可能性のある他の人があなたの答えを見つけやすくなります。
フィリップマーカー

3
コンポーネントまたは関数の外部のストアにアクセスしようとすると、「TypeError:WEBPACK_IMPORTED_MODULE_2__store_js .b is undefined」が表示されました。それがなぜであるかについて何か助けはありますか?
ベン

21

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

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

詳細については、Reduxのドキュメントをご覧ください。


19
保存storeするのはちょっとハッキーに聞こえるwindow
Vic

3
@Vic確かにそうです:)そして、通常、あなたはそれをしたくありません。この変数を使用して、あなたがやりたいことは何でもできることを述べたいと思います。おそらく、最良のアイデアは、 "createStore"ライフを作成したファイルに保存し、そこからインポートすることです。
ゴミ箱ジェネレータ2016年

他のiframeの状態にアクセスする必要がある複数のiframeがあります。ちょっとハッキリしているのはわかっていますが、メッセージをiframeに使用するよりも、ウィンドウ上にストアを置く方がいいと思います。何かご意見は??@Vic @trashgenerator?
Sean Malter


10

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

すべてのアクションでトークンが設定されますが、たとえば、トークンを変更するアクションのみをリッスンできます。


カスタムのaxiosインスタンスで同じことをどのように達成できますか?
アナトール

3

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

3
投票する場合は、コメントを追加してください。
Ogglas 2017

3
回答に説明がなく、JavaScriptではなくTypeScriptを使用しているため、反対票が投じられました。
ウィル

2
@Will理由を教えてくれてありがとう。イマオコードには仕様は必要ありませんが、具体的な説明が必要な場合は何と言ってください。TypeScriptは実際に使用されていますが、入力を削除すると、同じコードが問題なくES6で実行されます。
オグラス

サーバーサイドレンダリングを実行している場合、これは非常に悪いことに注意してください。すべてのリクエストで状態を共有します。
Rob Evans

2

少し遅いかもしれませんが、axios.interceptors以下のように使うのが一番いいと思います。インポートURLは、プロジェクトの設定に基づいて変更される場合があります。

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)
    );
}

1

フックでそれをやっています。同様の問題が発生しましたが、フック付きのreact-reduxを使用していました。ストアから/への情報の取得/送信専用の多くのコードを使用して、インターフェースコード(つまり、コンポーネントを反応させる)を積み上げたくありませんでした。むしろ、私はデータを取得して更新するための総称名を持つ関数が必要でした。私の道はアプリを置くことでした

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

名前のモジュールへstore.jsと加えるexportconstとstore.jsにおける通常の反応-Reduxの輸入を加えます。ファイル。次に、index.jsアプリレベルでにインポートし、通常import {store} from "./store.js"の子コンポーネントを使用してindex.jsにインポートした後、useSelector()およびuseDispatch()フックを使用してストアにアクセスしました。

非コンポーネントのフロントエンドコードでストアにアクセスするために、類似のインポート(つまりimport {store} from "../../store.js")を使用store.getState()してstore.dispatch({*action goes here*})から、を使用してストアの取得と更新(つまり、アクションの送信)を処理しました。


0

トークンにアクセスする簡単な方法は、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の場合Window.localStorage、AsyncStorageの代わりに使用できます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.