私は反応コンポーネント内でストアにアクセスしようとしているとき@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() {...}
}
洞察やワークフローのパターンはありますか?
CreateStoreを呼び出したモジュールからストアをエクスポートします。そうすれば、それは両方とも作成され、グローバルウィンドウスペースを汚染しないでしょう。
const store = createStore(myReducer);
export store;
または
const store = createStore(myReducer);
export default store;
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')
解決策を見つけました。だから私は私の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
多分それはさらに改善することができます、今のところそれは少し洗練されていないようです。後でできることは、私の店にミドルウェアを追加して、その場でトークンを設定することです。
store
関数から返されるcreateStore
オブジェクトを使用できます(これは、アプリの初期化のコードで既に使用されているはずです)。このオブジェクトを使用してstore.getState()
メソッドまたはstore.subscribe(listener)
メソッドで現在の状態を取得し、ストアの更新を購読することができます。
本当に必要な場合は、このオブジェクトをwindow
プロパティに保存して、アプリケーションの任意の部分からアクセスすることもできます(window.store = store
)
より詳しい情報は Reduxドキュメント にあります。
@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))
トークンはすべてのアクションに設定されますが、たとえばトークンを変更するアクションのみをリスンできます。
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(...)}
この質問はかなり古いものですが、私の考えを共有する価値があると思います。
トークンを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})を呼び出すようにしてください。
これは私が私のプロジェクトで使ったもう一つの選択肢です、そして私はあなたの考えを取りに行きたいです。
トークンにアクセスする簡単な方法は、トークンを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
を使用できます。