javascript - 如何在 `onClick` 事件中调用 Redux Saga 操作?

标签 javascript react-native redux-saga

我刚刚使用 infinitered/ignite 开始了一个新项目. 我已将我的 getUserToken 函数添加到 APITestScreen 所以我知道该函数按预期工作,但我无法将该方法与 onPress 函数连接到按钮 I added to the LaunchScreen .

我已经将它导入到 View 中,但是当我点击按钮时没有任何反应。我添加了一个警报和一个 console.log,但它们没有被触发。我应该怎么做才能在单击按钮时运行 fetchUserToken

整个项目发布posted at Github .

我的看法

 import getUserToken from '../Sagas/AuthSagas.js';
 <RoundedButton text="Fetch token" onPress={ getUserToken }  />

App/Redux/AuthRedux.js

import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  tokenRequest: ['username'],
  tokenSuccess: ['token'],
  tokenFailure: null
})

export const AuthTypes = Types
export default Creators

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  token: null,
  fetching: null,
  error: null,
  username: null
})

/* ------------- Reducers ------------- */

// request the token for a user
export const request = (state, { username }) =>
  state.merge({ fetching: true, username, token: null })

// successful token lookup
export const success = (state, action) => {
  const { token } = action
  return state.merge({ fetching: false, error: null, token })
}

// failed to get the token
export const failure = (state) =>
  state.merge({ fetching: false, error: true, token: null })

/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.TOKEN_REQUEST]: request,
  [Types.TOKEN_SUCCESS]: success,
  [Types.TOKEN_FAILURE]: failure
})

App/Sagas/AuthSagas.js

import { call, put } from 'redux-saga/effects'
import { path } from 'ramda'
import AuthActions from '../Redux/AuthRedux'

export function * getUserToken (api, action) {
  console.tron.log('Hello, from getUserToken');
  alert('in getUserToken');
  const { username } = action
  // make the call to the api
  const response = yield call(api.getUser, username)

  if (response.ok) {
    const firstUser = path(['data', 'items'], response)[0]
    const avatar = firstUser.avatar_url
    // do data conversion here if needed
    yield put(AuthActions.userSuccess(avatar))
  } else {
    yield put(AuthActions.userFailure())
  }
}

Sagas/index.js

export default function * root () {
  yield all([
    // some sagas only receive an action
    takeLatest(StartupTypes.STARTUP, startup),

    // some sagas receive extra parameters in addition to an action
    takeLatest(GithubTypes.USER_REQUEST, getUserAvatar, api),

    // Auth sagas
    takeLatest(AuthTypes.TOKEN_REQUEST, getUserToken, api)
  ])
}

最佳答案

Sagas 很棒,因为它们允许长时间运行的进程以完全分离的方式控制应用程序流,并且可以通过操作排序,允许您并行化/取消/fork/协调 sagas 以在一个集中的地方编排您的应用程序逻辑(即认为它能够将 Action 链接在一起,并在此过程中合并副作用)

通过导入生成器函数并像普通函数一样直接调用它是行不通的,如果这样做会绕过 saga 功能,例如,如果您第二次或第三次按下该按钮,它将始终执行整个生成器从头到尾再次运行,因为它们涉及异步操作,这可能导致您说尝试存储或使用 token ,然后该 token 立即被后续 saga 无效

更好的做法是让您的 saga 始终监听特定的操作以触发更多的 worker sagas,保持它们解耦,并允许它们控制自己的流程。

在这种情况下,您将发送一个操作 onPress,并让一个长时间运行的父 saga 监听该操作,然后将其交给您当前的操作来完成实际工作。然后,此监听 saga 将控制取消先前的调用,使用 takeLatest 将取消先前的 saga 调用,以便在前一个仍在运行时按下后续按钮将始终优先,并且您的 token 不能不小心变质了

//AuthActions.js

// add a new action (or more probably adapt fetchUserToken to suit)...
export const GET_USER_TOKEN = 'auth/get-user-token'
export const getUserToken = (username) => ({
  type: GET_USER_TOKEN, 
  payload: username
})

// View

import {getUserToken} from './AuthActions'

// this now dispatches action (assumes username is captured elsewhere)
// also assumes store.dispatch but that would more likely be done via `connect` elsewhere
<RoundedButton text="Fetch token" onPress={ () => store.dispatch(getUserToken(this.username)) }  />

//AuthSagas.js

import api from 'someapi'
import actions from 'someactions'
import {path} from 'ramda'
import {put, call, takeLatest} from 'redux-saga/effects'
import AuthActions from '../Redux/AuthRedux'

// this will be our long running saga
export function* watchRequestUserToken() {
  // listens for the latest `GET_USER_TOKEN` action, 
  // `takeLatest` cancels any currently executing `getUserToken` so that is always up to date
  yield takeLatest(AuthActions.GET_USER_TOKEN, getUserToken)
}

// child generator is orchestrated by the parent saga
// no need to export (unless for tests) as it should not be called by anything outside of the sagas
function* getUserToken (action) { // the actual action is passed in as arg
  const username = action.payload
  // make the call to the api
  const response = yield call(api.getUser, username)

  if (response.ok) {
    const firstUser = path(['data', 'items'], response)[0]
    const avatar = firstUser.avatar_url
    // do data conversion here if needed
    yield put(AuthActions.userSuccess(avatar))
  } else {
    yield put(AuthActions.userFailure())
  }
}

//main.js(例子取自https://redux-saga.js.org/)适配套件

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'

import {reducer} from './AuthRedux'
import {watchRequestUserToken} from './AuthSagas'

// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
export const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

// then run the saga
sagaMiddleware.run(watchRequestUserToken)

关于javascript - 如何在 `onClick` 事件中调用 Redux Saga 操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46905328/

相关文章:

javascript - 组织大型 sproutcore 项目的推荐方法是什么?

javascript - 有没有办法改进reduce的这种使用?

JavaScript : Is using 'new' every time for a variable the correct way to change its value repeatedly?

reactjs - 如何确保 Redux Saga 中数据不会加载两次?

javascript - React store.getState 不是函数

javascript - 当使用 redux saga 和 React 时,状态如何在 sagas.js 中获取

javascript - 下载pdf之前在表格中制作div内容

php - 获取 native react 不发送帖子

react-native - 如何将 Store 共享到 TabNavigator

React-native 0.60 - Android 构建发布失败 - OutOfMemory