你如何使用 redux get 函数更新一些状态?值在片场没有正确更新

IT技术 reactjs redux redux-saga
2021-05-17 13:53:00

我目前正在通过 redux getBalances 方法获取一些余额。当应用程序初始化时,它将“余额”设置为信息的 JSON,但是当我再次调用 getBalances 时,它不会重新设置余额(不知道为什么)。

所以现在,我正在手动尝试通过调用 getBalances 方法然后将结果设置为 balances 来更新余额,但是我遇到了障碍。

我想要做的就是再次 getBalances 并仅将其设置为 balances,但是我不确定如何在 redux 中执行此操作。

// Sequence of events (all of these are in different files of course)    

// Action Call
export const getBalances = exchange => 
action(actionTypes.GET_BALANCES.REQUEST, { exchange })

// API Call
export const getBalances = ({ userId, exchange }) =>
API.request(`/wallets/${userId}/${exchange}`, 'GET')

完整的传奇

          import { fork, takeEvery, takeLatest, select, put, call, throttle } from 'redux-saga/effects'
          import { NavigationActions } from 'react-navigation'

          import * as actionTypes from '../action-types/exchanges.action-types'
          import * as API from '../api'

          import { storeType } from '../reducers'
          import { async, delay } from './asyncSaga'
          import { asyncAction } from './asyncAction'

          let getBalanceCount = 0

          export function* getBalances(action) {
          getBalanceCount++
          const state: storeType = yield select()
          yield fork(async, action, API.getBalances, {
            exchange: state.exchanges.selectedExchange._id,
            userId: state.auth.userId,
          })
          if (getBalanceCount > 1) {
            getBalanceCount--
            return
          }
          yield delay(10000)
          if (state.auth.token && state.auth.status === 'success')
            yield put({ type: action.type, payload: {} })
          /*
          if (state.auth.token && state.auth.status === 'success' && state.auth.phoneVerified)
            yield put({ type: action.type, payload: {} }) */
          }

          export function* getExchanges(action) {
          const state: storeType = yield select()
          yield fork(async, action, API.getExchanges, { userId: state.auth.userId })
          }

          export function* getExchangesSuccess(action) {
          const state: storeType = yield select()
          if (state.exchanges.exchanges.length > 0) {
            yield put({ type: actionTypes.GET_BALANCES.REQUEST, payload: {} })
          }
          }

          export function* addExchange(action) {
          const state: storeType = yield select()
          yield fork(async, action, API.addExchange, { ...action.payload, userId: state.auth.userId })
          }

          export function* addExchangeSuccess(action) {
          yield put(
            NavigationActions.navigate({
              routeName: 'wallets',
              params: { transition: 'slideToTop' },
            }),
          )
          }

          export function* updatePrices(action) {
          const async = asyncAction(action.type)
          const state = yield select()
          try {
            const res = yield call(API.getSymbolPriceTicker)
            yield put(async.success(res))
          } catch (error) {
            yield put(async.failure(error))
          }
          yield delay(10000)
          if (state.auth.token && state.auth.status === 'success' && state.auth.phoneVerified)
            yield put({ type: action.type, payload: {} })
          }

          export function* updateDaily(action) {
          const async = asyncAction(action.type)
          try {
            const res = yield call(API.getdayChangeTicker)
            yield put(async.success(res))
          } catch (error) {
            yield put(async.failure(error))
          }
          }

          export function* getFriendExchange(action) {
          yield fork(async, action, API.getExchanges, { userId: action.payload.userId })
          }

          export function* selectExchange(action) {
          yield put({ type: actionTypes.GET_BALANCES.REQUEST, payload: {} })
          }

          export function* exchangesSaga() {
          yield takeEvery(actionTypes.GET_SYMBOL_PRICE_TICKER.REQUEST, updatePrices)
          yield takeEvery(actionTypes.GET_DAY_CHANGE_TICKER.REQUEST, updateDaily)
          yield takeLatest(actionTypes.GET_FRIEND_EXCHANGES.REQUEST, getFriendExchange)
          yield takeLatest(actionTypes.GET_BALANCES.REQUEST, getBalances)
          yield takeLatest(actionTypes.GET_EXCHANGES.REQUEST, getExchanges)
          yield takeLatest(actionTypes.GET_EXCHANGES.SUCCESS, getExchangesSuccess)
          yield takeLatest(actionTypes.ADD_EXCHANGE.REQUEST, addExchange)
          yield takeLatest(actionTypes.ADD_EXCHANGE.SUCCESS, addExchangeSuccess)
          yield takeLatest(actionTypes.SELECT_EXCHANGE, selectExchange)
          }

全交换减速机

    import { mergeDeepRight } from 'ramda'
    import {
      GET_BALANCES,
      GET_EXCHANGES,
      SELECT_EXCHANGE,
      GET_SYMBOL_PRICE_TICKER,
      GET_DAY_CHANGE_TICKER,
      GET_FRIEND_EXCHANGES,
      ADD_EXCHANGE,
    } from '../action-types/exchanges.action-types'
    import { LOG_OUT, VALIDATE_TOKEN } from '../action-types/login.action-types'
    import { ExchangeService } from '../constants/types'

    // Exchanges Reducer

    export type exchangeState = {
      status: string
      _id: string
      label: string
      displayName: string
      dayChangeTicker: any
      symbolPriceTicker: any
      balances: any,
    }

    export type exchangesState = {
      status: string
      selectedExchange: exchangeState
      addExchange: {
        status: string,
      }
      exchanges: Array<ExchangeService>
      friendExchanges: Array<ExchangeService>,
    }

    const initialExchangeState: exchangeState = {
      status: 'pending',
      _id: '',
      label: '',
      displayName: null,
      dayChangeTicker: {},
      symbolPriceTicker: {},
      balances: {},
    }

    const initialState: exchangesState = {
      status: 'pending',
      selectedExchange: {
        status: 'pending',
        _id: '',
        label: '',
        displayName: null,
        dayChangeTicker: {},
        symbolPriceTicker: {},
        balances: {},
      },
      addExchange: {
        status: 'pending',
      },
      exchanges: [],
      friendExchanges: [],
    }

    export default (state = initialState, action) => {
      switch (action.type) {
        case SELECT_EXCHANGE:
        case GET_SYMBOL_PRICE_TICKER.SUCCESS:
        case GET_DAY_CHANGE_TICKER.SUCCESS:
        case GET_BALANCES.REQUEST:
        case GET_BALANCES.SUCCESS:
        case GET_BALANCES.FAILURE:
          return { ...state, selectedExchange: selectedExchangeReducer(state.selectedExchange, action) }

        case GET_EXCHANGES.REQUEST:
        case GET_FRIEND_EXCHANGES.REQUEST:
          return { ...state, status: 'loading' }

        case GET_EXCHANGES.SUCCESS:
          if (action.payload.exchanges.length > 0) {
            return mergeDeepRight(state, {
              exchanges: action.payload.exchanges,
              selectedExchange: { ...action.payload.exchanges[0] },
              status: 'success',
            })
          }
          return { ...state, status: 'success' }

        case GET_FRIEND_EXCHANGES.SUCCESS:
          return { ...state, friendExchanges: action.payload.exchanges, status: 'success' }

        case GET_EXCHANGES.FAILURE:
        case GET_FRIEND_EXCHANGES.FAILURE:
          return { ...state, message: action.payload.message, status: 'failure' }

        case LOG_OUT.SUCCESS:
        case VALIDATE_TOKEN.FAILURE:
          return initialState

        case ADD_EXCHANGE.REQUEST:
          return { ...state, addExchange: { status: 'loading' } }

        case ADD_EXCHANGE.SUCCESS:
          return { ...state, addExchange: { status: 'success' } }

        case ADD_EXCHANGE.FAILURE:
          return { ...state, addExchange: { status: 'failure' } }

        default:
          return state
      }
    }

    const selectedExchangeReducer = (state = initialExchangeState, action) => {
      switch (action.type) {
        case SELECT_EXCHANGE:
          if (action.payload.exchange) {
            return { ...state, ...action.payload.exchange }
          }
          return initialExchangeState

        case GET_SYMBOL_PRICE_TICKER.SUCCESS:
          const symbolPriceTicker = action.payload.data.data.reduce((result, ticker) => {
            result[ticker.symbol] = ticker.price
            return result
          }, {})
          return { ...state, symbolPriceTicker }

        case GET_DAY_CHANGE_TICKER.SUCCESS:
          const dayChangeTicker = action.payload.data.data.reduce((result, ticker) => {
            result[ticker.symbol] = ticker.priceChangePercent
            return result
          }, {})
          return { ...state, dayChangeTicker }

        // Get selected exchange's balances
        case GET_BALANCES.REQUEST:
          return { ...state, status: 'loading' }

        case GET_BALANCES.SUCCESS:
          return {
            ...state,
            balances: action.payload.balances,
            status: 'success',
          }

        case GET_BALANCES.FAILURE:
          return { ...state, balances: [], message: action.payload.message, status: 'failure' }

        default:
          return state
      }
    }

物理函数调用(fetchData 是我尝试重新分配 exchange.balances ...)

    // this.props.selectExchange(exchange) just selects the exchange then calls a GET_BALANCES.REQUEST
    fetchData = (exchange) => {
      const { selectedExchange } = this.props.exchanges
      // const { exchanges } = this.props
      // //console.log('TesterTesterTester: ' + JSON.stringify(this.props.selectExchange(exchange)))
      // console.log('Test:' + JSON.stringify(this.props.getBalances(exchange.balances)))
      // let vari = JSON.stringify(this.props.getBalances(exchange.balances))
      // let newVari = JSON.parse(vari.slice(45, vari.length-2))
      // exchange.balances = newVari
      // console.log('Old Values: ' + JSON.stringify(exchange.balances))
      console.log('Testt: ' + JSON.stringify(this.props.selectExchange(exchange.balances1)))
      this.props.selectExchange(exchange.balances1)
      console.log('This exchange after: ' + selectedExchange)
      console.log('This is the balances: '+ JSON.stringify(selectedExchange.balances1))
      exchange.balances = selectedExchange.balances1
      console.log('Another one: ' + JSON.stringify(exchange.balances))
      selectedExchange.balances1 = []

      this.setState({ refreshing: false })
    }

    renderExchange = (exchange, index) => {
      const { refreshing } = this.state
      const { selectedExchange } = this.props.exchanges
      const { symbolPriceTicker, dayChangeTicker } = selectedExchange

      // I'm trying to alter exchange.balances

      if (refreshing) {
        this.fetchData(exchange)
      }

      return (
        <View style={screenStyles.container}>
          <ExchangeBox
            balances={exchange.balances}
            displayName={exchange.label}
            symbolPriceTicker={symbolPriceTicker}
            exchangeIndex={index}
            onSend={this.onSend}
          />
          <View style={screenStyles.largerContainer}>
            {symbolPriceTicker && dayChangeTicker && exchange.balances && (
              <ScrollView
                style={screenStyles.walletContainer}
                horizontal={true}
                showsHorizontalScrollIndicator={false}
                decelerationRate={0}
                snapToInterval={100} //your element width
                snapToAlignment={'center'}
              >
                {Object.keys(exchange.balances).map(
                  symbol =>
                    COIN_INFO[symbol] &&
                    symbolPriceTicker[`${symbol}USDT`] && (
                      <CoinContainer
                        key={symbol}
                        symbol={symbol}
                        available={exchange.balances[symbol].free}
                        price={symbolPriceTicker[`${symbol}USDT`]}
                        dayChange={dayChangeTicker[`${symbol}USDT`]}
                      />
                    ),
                )}
              </ScrollView>
            )}
          </View>
        </View>
      )
    }

在弄乱了这个之后,我发现 exchange.balances 没有抓取值,因为 .balances 是交换 JSON 的 JSON 扩展。我尝试在其他地方制作所有 balances 实例(例如在 reducer balances1 中),但在尝试更新时并没有太大帮助。

这是 types.ts 中的另一个余额调用

  export type ExchangeService = {
    _id: string
    label: string
    displayName: string
    balances: any,
  }

非常感谢@Dylan 和我一起走过这一切

1个回答

正如评论中所讨论的:

您对如何通过fetchData. 看来您正在尝试分派一个动作并在同一渲染周期中使用结果,这充其量使用 Redux 会产生不一致的结果。

相反,当将 Redux 与 React 结合使用时,您应该几乎完全依赖 Redux 来处理您的状态管理。根据以下数据流,您的 React 组件应仅用于分派 Redux 操作和显示传入数据:

  1. 组件向商店分派一个动作。
  2. 该操作由您的 saga 和 reducer 处理以更新 store 中的状态。
  3. Redux 更新通过connect().
  4. 更新后的 props 会触发组件的重新渲染。
  5. 更新后的状态,现在提供给组件通过this.propsrender()功能上触发渲染周期。

由于这与您的组件有关,因此您的fetchData功能可能会简化为以下内容:

fetchData = exchange => {
    this.props.selectExchange(exchange);
    // ...any additional action dispatches required to fetch data.
}

如果您的 reducer 和 saga 编写正确(它们看起来是这样),那么您的 Redux 状态将异步更新。更新完成后,您的组件props将更新并触发重新渲染。然后,在您的render()函数中,您从状态显示的所有数据都应从this.props. 通过这样做,您可以在很大程度上保证显示是最新的:

render() {
    const exchange = this.props.selectedExchange;

    return (
        <View style={screenStyles.container}>
            <ExchangeBox
                balances={exchange.balances}
                displayName={exchange.label}
                // ... more props
            />
            //... more components
        </View>
    );
}

此时,您的组件已使用简单且惯用的 Redux 数据流进行设置。如果您在此时遇到任何状态更新问题,您可以开始查看您的 sagas/reducer 是否有问题。

以下是我的原始答案,其中讨论了已发布的传奇的潜在问题,为了完整起见,我将保留这些问题。


感谢您的澄清编辑。我花了一段时间才理解,因为这个传奇结构真的很不寻常,但我想我对这里发生的事情有一个想法。如果我做出任何错误的假设,请纠正我。

yield delay(10000)
if (state.auth.token && state.auth.status === 'success')
    yield put({ type: action.type, payload: {} })

我认为这样做的目的是balances在传奇开始后每 10 秒更新一次。我还假设您必须getBalancesCount限制getBalances一次循环的实例数让我们来看看这是如何发生的:

  • 初始调度 ->yield takeLatest(actionTypes.GET_BALANCES.REQUEST, getBalances)开始getBalances
  • getBalances命中getBalanceCount++,所以getBalanceCount == 1
  • getBalances 重复,由于 put({ type: action.type, payload: {} })
  • getBalances命中getBalanceCount++,所以getBalanceCount == 2
  • getBalances命中if (getBalanceCount > 1),满足条件,递减getBalanceCount1和退出。

现在,我假设yield fork(async, action, API.getBalances...)最终GET_BALANCES.SUCCESS在 中分派asyncSaga,因此每次您GET_BALANCES.REQUEST从传奇外部分派时它都会继续工作

您可以修复getBalancesCount. 但是,我们根本不需要计数器来限制同时getBalances运行的并发数这已经内置于takeLatest

每次将操作分派到商店时。如果此操作匹配pattern,则在后台takeLatest启动新saga任务。如果saga任务先前已启动(在实际操作之前调度的最后一个操作上),并且该任务仍在运行,则该任务将被取消

(参见:https : //redux-saga.js.org/docs/api/

所以你真正需要做的就是删除你的自定义逻辑:

export function* getBalances(action) {
    const state: storeType = yield select()
    yield fork(async, action, API.getBalances, {
        exchange: state.exchanges.selectedExchange._id,
        userId: state.auth.userId,
    })

    yield delay(10000)
    if (state.auth.token && state.auth.status === 'success')
        yield put({ type: action.type, payload: {} })
    }
}

此外,通过从 saga 内部调度相同的 action 来重复 saga 是一种反模式。 while(true)尽管看起来很奇怪,但往往更惯用:

export function* getBalances(action) {
    while(true) {
        const state: storeType = yield select()
        yield fork(async, action, API.getBalances, {
            exchange: state.exchanges.selectedExchange._id,
            userId: state.auth.userId,
        })

        yield delay(10000);
        if (!state.auth.token || state.auth.status !== 'success')
           return;
        }
    }
}

但是,如果您GET_BALANCES.REQUEST出于某种原因需要消耗其他东西,这可能对您不起作用。在那种情况下,我会使用单独的操作。(编辑:我重新阅读了您的减速器,您确实在使用该操作来设置loading状态。在这种情况下,您的方法可能很好。)