Cách tốt nhất để truy cập cửa hàng redux bên ngoài một thành phần phản ứng là gì?


191

@connecthoạt động tuyệt vời khi tôi đang cố gắng truy cập vào cửa hàng trong một thành phần phản ứng. Nhưng làm thế nào tôi nên truy cập nó trong một số mã khác. Ví dụ: giả sử tôi muốn sử dụng mã thông báo ủy quyền để tạo phiên bản axios có thể được sử dụng trên toàn cầu trong ứng dụng của mình, cách tốt nhất để đạt được điều đó là gì?

Đây là của tôi 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

Bây giờ tôi muốn truy cập một điểm dữ liệu từ cửa hàng của mình, đây là giao diện nếu tôi cố tìm nạp nó trong một thành phần phản ứng bằng cách sử dụng @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() {...}
}

Bất kỳ hiểu biết hoặc mô hình quy trình công việc ra khỏi đó?


Bạn không muốn cá thể Axios của bạn sống trong một phần mềm trung gian redux? Nó sẽ có sẵn cho tất cả các ứng dụng của bạn theo cách này
Emrys Myrooin

Bạn có thể nhập lớp apitrong Appvà sau khi nhận được mã thông báo ủy quyền bạn có thể làm api.defaults.headers.common['Authorization'] = this.props.auth.tokens.authorization_token;, đồng thời bạn cũng có thể lưu trữ trong localStorage, vì vậy khi người dùng làm mới trang, bạn có thể kiểm tra xem mã thông báo có tồn tại trong localStorage không và nếu nó không, bạn có thể thiết lập nó. Tôi nghĩ rằng tốt nhất là đặt mã thông báo trên mô-đun api ngay khi bạn nhận được nó.
Dhruv Kumar Jha

1
Dan Abromov cung cấp một ví dụ trong hàng đợi vấn đề tại đây: github.com/reactjs/redux/issues/916
chrisjlee

1
Nếu bạn chỉ cần truy cập vào một trạng thái cụ thể từ một bộ giảm tốc cụ thể, bạn có thể thử với các bộ giảm tốc có tên redux, nó cho phép bạn truy cập trạng thái mới nhất từ ​​bất cứ đâu.
miles_christian

Câu trả lời:


146

Xuất cửa hàng từ mô-đun bạn gọi createStorevới. Sau đó, bạn được đảm bảo rằng cả hai sẽ được tạo và sẽ không gây ô nhiễm không gian cửa sổ toàn cầu.

MyStore.js

const store = createStore(myReducer);
export store;

hoặc là

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

MyClient.js

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

hoặc nếu bạn sử dụng mặc định

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

Đối với nhiều trường hợp sử dụng

Nếu bạn cần nhiều phiên bản của một cửa hàng, hãy xuất một hàm xuất xưởng. Tôi sẽ khuyên bạn nên làm nó async(trả lại a promise).

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

Trên máy khách (trong một asynckhối)

import {getUserStore} from './store'

const joeStore = await getUserStore('joe')

47
CẢNH BÁO cho các ứng dụng đẳng cấu: làm phía máy chủ này sẽ chia sẻ cửa hàng redux trên tất cả người dùng của bạn !!!
Lawrence Wagerfield

6
Câu hỏi cũng không nói rõ "trình duyệt". Vì cả Redux và JavaScript đều có thể được sử dụng trên cả máy chủ hoặc trình duyệt, nên cụ thể sẽ an toàn hơn nhiều.
Lawrence Wagerfield

7
cửa hàng xuất khẩu dường như tạo ra cơn ác mộng về nhập khẩu theo chu kỳ, createdStore bao gồm các bộ giảm tốc của bạn, yêu cầu các hành động của bạn (ít nhất là enum loại hành động và giao diện Hành động), không được nhập bất cứ thứ gì cố gắng nhập cửa hàng. Vì vậy, bạn không được sử dụng cửa hàng trong các bộ giảm tốc hoặc hành động của mình (hoặc thay đổi một số cách khác xung quanh việc nhập theo chu kỳ.)
Eloff

6
Đây là câu trả lời đúng, nhưng nếu bạn (như tôi) muốn đọc thay vì gửi một hành động trong cửa hàng, bạn cần gọistore.getState()
Juan Jose Ramírez

3
Vâng, giải thích của tôi về "truy cập cửa hàng redux" là "đọc" cửa hàng. Chỉ cố gắng giúp đỡ người khác.
Juan Jose Ramírez

47

Tìm thấy một giải pháp. Vì vậy, tôi nhập cửa hàng trong ứng dụng api của tôi và đăng ký vào đó. Và trong chức năng trình nghe đó, tôi đặt mặc định toàn cầu của axios với mã thông báo mới được tìm nạp.

Đây là những gì mới của tôi api.jstrông như:

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

Có lẽ nó có thể được cải thiện hơn nữa, vì hiện tại nó có vẻ hơi không phù hợp. Những gì tôi có thể làm sau này là thêm một phần mềm trung gian vào cửa hàng của tôi và đặt mã thông báo sau đó ở đó.


1
bạn có thể chia sẻ những gì store.jstập tin của bạn trông như thế nào?
Vic

chính xác một cái gì đó tôi đang tìm kiếm, cảm ơn một tấn @subodh
Harkirat Saluja

1
Tôi biết câu hỏi đã cũ, nhưng bạn có thể chấp nhận câu trả lời của mình là chính xác. Điều này làm cho câu trả lời của bạn dễ dàng tìm thấy hơn cho những người khác cuối cùng có thể đến đây.
Filipe Merker

3
Tôi đã nhận được "TypeError: WEBPACK_IMPORTED_MODULE_2__store_js .b không xác định" khi tôi cố gắng truy cập vào cửa hàng bên ngoài một thành phần hoặc chức năng. Bất kỳ trợ giúp về lý do đó là?
ben

21

Bạn có thể sử dụng stoređối tượng được trả về từ createStorehàm (vốn đã được sử dụng trong mã của bạn khi khởi tạo ứng dụng). Hơn bạn có thể sử dụng đối tượng này để có được trạng thái hiện tại với store.getState()phương thức hoặc store.subscribe(listener)đăng ký để lưu trữ các bản cập nhật.

Bạn thậm chí có thể lưu đối tượng này vào thuộc windowtính để truy cập nó từ bất kỳ phần nào của ứng dụng nếu bạn thực sự muốn nó ( window.store = store)

Thông tin thêm có thể được tìm thấy trong tài liệu Redux .


19
Nghe có vẻ khó tin để tiết kiệm storechowindow
Vic

3
@Vic Chắc chắn là như vậy :) Và thông thường bạn không muốn làm điều đó. Tôi chỉ muốn đề cập rằng bạn có thể làm bất cứ điều gì bạn muốn với biến này. Có lẽ ý tưởng tốt nhất sẽ là lưu trữ nó trong tệp nơi bạn đã tạo cuộc sống "createdStore" của mình và sau đó chỉ cần nhập từ nó.
thùng rác

Tôi có nhiều iframe cần truy cập vào trạng thái của các iframe khác. Tôi biết đó là một vụ hack nhưng tôi nghĩ rằng có cửa hàng trên cửa sổ sẽ tốt hơn so với việc sử dụng tin nhắn đến iframe. Có suy nghĩ gì không ?? @Vic @trashgenerator?
Sean Malter


9

Giống như @sanchit đề xuất phần mềm trung gian là một giải pháp tốt nếu bạn đã xác định thể hiện axios của mình trên toàn cầu.

Bạn có thể tạo một phần mềm trung gian như:

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;

Và sử dụng nó như thế này:

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

Nó sẽ đặt mã thông báo cho mọi hành động nhưng bạn chỉ có thể lắng nghe các hành động thay đổi mã thông báo chẳng hạn.


3

Đối với TypeScript 2.0, nó sẽ trông như thế này:

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
Nếu bạn đang bỏ phiếu xuống, xin vui lòng thêm một nhận xét tại sao.
Ogglas

3
Bỏ phiếu vì câu trả lời của bạn thiếu phần giải thích và sử dụng TypeScript thay vì Javascript.
Sẽ

2
@ Sẽ cảm ơn bạn đã nói lý do tại sao. Imao mã không yêu cầu đặc điểm kỹ thuật nhưng nếu bạn muốn một cái gì đó được giải thích cụ thể xin vui lòng nói những gì. TypeScript thực sự được sử dụng nhưng nếu các kiểu chữ được loại bỏ thì cùng một mã sẽ chạy trong ES6 mà không gặp vấn đề gì.
Ogglas

Hãy nhớ rằng điều này sẽ rất tệ nếu bạn đang thực hiện kết xuất mặt phẳng, nó sẽ chia sẻ trạng thái với tất cả các yêu cầu.
Rob Evans

2

Có thể hơi muộn nhưng tôi nghĩ cách tốt nhất là sử dụng axios.interceptorsnhư dưới đây. Nhập url có thể thay đổi dựa trên thiết lập dự án của bạn.

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

Làm điều đó với móc. Tôi gặp phải một vấn đề tương tự, nhưng tôi đang sử dụng Reac-redux với hook. Tôi không muốn làm mất mã giao diện của mình (nghĩa là các thành phần phản ứng) với rất nhiều mã dành riêng để truy xuất / gửi thông tin từ / đến cửa hàng. Thay vào đó, tôi muốn các hàm có tên chung để lấy và cập nhật dữ liệu. Con đường của tôi là đặt ứng dụng

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

vào một mô-đun được đặt tên store.jsvà thêm exporttrước đó constvà thêm các lần nhập phản ứng-redux thông thường trong store.js. tập tin. Sau đó, tôi đã nhập vào index.jsở cấp ứng dụng, sau đó tôi đã nhập vào index.js với các import {store} from "./store.js"thành phần con thông thường sau đó truy cập vào cửa hàng bằng cách sử dụng useSelector()useDispatch()hook.

Để truy cập vào cửa hàng theo mã mặt trước không phải thành phần, tôi đã sử dụng nhập tương tự (nghĩa là import {store} from "../../store.js"), sau đó sử dụng store.getState()store.dispatch({*action goes here*})để xử lý truy xuất và cập nhật (er, gửi hành động đến) cửa hàng.


0

Một cách dễ dàng để có quyền truy cập vào mã thông báo, là đặt mã thông báo vào LocalStorage hoặc AsyncStorage với React Native.

Dưới đây là một ví dụ với dự án 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 };

Đối với web, bạn có thể sử dụng Window.localStoragethay vì AsyncStorage

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.