React-Native / Redux调度在动作中多次触发

时间:2018-07-23 13:23:25

标签: javascript reactjs react-native redux

我正在制作一个React / Redux应用程序。在我的一项操作中,dispatch在无明显原因的情况下被射击6至8次。请参阅下面有关我的组件的操作文件中的addMarkersRequestAddress

export function addMarkersSuccess(response) {
  return {
    type: 'addMarkersSuccess',
    status: 'success',
    response: response,
    receivedAt: Date.now(),
  };
}

export function addMarkersFailure(error) {
  return {
    type: 'addMarkersFailure',
    status: 'error',
    error: error,
    receivedAt: Date.now(),
  };
}

export function addMarkersRequestCoordinates(submitFormData) {


  // Why is this always returning addMarkersFailure? Is it possibly related to why it always fires multiple times?
  // Same code as in virtualFenceWalk actions
  return (dispatch) => {

    console.log('running addMarkersRequestCoordinates');
    console.log('submitFormData: ',submitFormData);

    let JSONbody = JSON.stringify(submitFormData);
    console.log('JSONbody: ',JSONbody);

    fetch('http://localhost:8080/virtualFence', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSONbody
        }).then(function(response){
          dispatch(addMarkersSuccess(response));
        }).catch(function(error) {
          dispatch(addMarkersFailure(error));
        });

  }
}

export function addMarkersRequestAddress(submitFormData) {
  return (dispatch) => {

    console.log('running addMarkersRequestAddress');
    console.log('submitFormData: ',submitFormData);

    let JSONbody = JSON.stringify(submitFormData);
    console.log('JSONbody: ',JSONbody);

    // Make a request to a backend route that gets the coordinates from the Google Maps API
    fetch('http://localhost:8080/virtualFenceAddress', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSONbody
        }).then(function(response){
          console.log('addMarkersRequestAddress success');
          console.log('response: ',response);
          dispatch(addMarkersSuccess(response));
        }).catch(function(error) {
          console.log('addMarkersRequestAddress failure');
          console.log('error: ',error);
          dispatch(addMarkersFailure(error));
        });

  }

}

此代码运行时,addMarkersSuccess将触发6-8次。具体来说,它与dispatch有关,因为如果我删除了dispatch调用并仅保留控制台日志,addMarkersSuccess就会按预期触发一次,仅此而已。它似乎也与fetch或异步性无关,因为如果删除fetch并在函数的主体中尝试相同的事情,则会发生相同的结果。

这里是围绕组件包装的容器(因为我将其范围缩小到了如何调用dispatch的问题,因为没有dispatch的操作的其他部分只会触发一次,也许在那里dispatch在这里的设置有问题吗?):

import React, { Component }                                             from 'react';
import PropTypes                                                        from 'prop-types';
import { StyleSheet, View, Text, TouchableOpacity, TouchableHighlight } from 'react-native';
import { bindActionCreators }                                           from 'redux';
import { connect }                                                      from 'react-redux';
import VirtualFence                                                     from '../components/VirtualFence';
import * as VirtualFenceActions                                         from '../actions/virtualFence';

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
  back: {
    margin: 10,
    fontSize: 20,
  },
});

// Map the Redux state to props
@connect(
  state => ({
    bigState: state,
    markers: state.markers,
  }),
  dispatch => bindActionCreators(VirtualFenceActions, dispatch),
)

export default class VirtualFenceContainer extends Component {

  render() {
    return (
      <View style={styles.container}>
        <VirtualFence {...this.props} />
      </View>
    );
  }
}

在此处是在组件本身中调用动作的地方:

render() {

    const {
      addMarkersRequestAddress, addMarkersSuccess, addMarkersFailure
    } = this.props;

    return (
      <View>
        <TouchableOpacity onPress={this.toggleModal}>
          <Text style={styles.bottomText}>Add markers by street address</Text>
        </TouchableOpacity>
        <Modal isVisible={this.state.isVisible}>
          <View style={{ flex: 1 }}>
            <TouchableOpacity onPress={this.toggleModal}>
              <Text style={styles.bottomText}>Hide me!</Text>
            </TouchableOpacity>
            <Form
              ref="form"
              type={Points}
              options={pointsOptions}
            />
            <Button title="Add form field" onPress={this.addFormField}></Button>
            <Button title="Delete form field" onPress={this.deleteFormField}></Button>
            <Button
              title="Submit markers"
              onPress={(argument)=>addMarkersRequestAddress(this.refs.form.getValue())}
            />
          </View>
        </Modal>
      </View>
    );
  }

虽然没有回答我的问题,但这里和其他地方的其他一些答案似乎暗示着解决方案可能与我的configureStore.js文件有关,所以这里是:

/* eslint global-require: 0 */

import { Platform } from 'react-native';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';

// Presumably I need to add the other action files here somehow? Nothing seems to change as long as one file is listed...
import * as actionCreators from './actions/activityTracker';

let composeEnhancers = compose;
if (__DEV__) {
  // Use it if Remote debugging with RNDebugger, otherwise use remote-redux-devtools
  /* eslint-disable no-underscore-dangle */
  composeEnhancers = (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||
    require('remote-redux-devtools').composeWithDevTools)({
    name: Platform.OS,
    ...require('../package.json').remotedev,
    actionCreators,
  });
  /* eslint-enable no-underscore-dangle */
}

const enhancer = composeEnhancers(applyMiddleware(thunk));

// I think the problem with multiple dispatches may be in here
// See https://stackoverflow.com/questions/49734848/redux-dispatch-fires-multiple-times
export default function configureStore(initialState) {
  const store = createStore(reducer, initialState, enhancer);
  if (module.hot) {
    module.hot.accept(() => {
      store.replaceReducer(require('./reducers').default);
    });
  }
  return store;
}

请注意,我真的不知道此文件在做什么。我使用react-native-boilerplate开始了该应用程序,因此该文件是从那里获取的。如果需要在此处进行更改,那么如果您能详细说明这些更改的作用,将不胜感激。

编辑1:最初撰写此帖子时,在第一个引发错误后进行所有调度。在应用程序的其他部分进行了进一步的工作之后,所有的触发操作现在都已成功记录。但是,仍然存在基本问题(多次点火的原因)。

编辑2:添加了围绕组件包装的容器。

4 个答案:

答案 0 :(得分:1)

问题的根源出在我调用combineReducers helper函数的文件中。我不怀疑此文件与问题有关,因此没有发布它。对于在初始状态对象中具有多个键的组件,我错误地认为我必须为每个键进行导入,而实际上我需要为每个reducer文件进行一次导入。我从virtualFence减速器中导入了六个变量,每个变量都导致dispatch触发。

这是不正确的版本:

import { combineReducers }       from 'redux';
import nav                       from './nav';
import virtualFence              from './virtualFence';
import latitude                  from './virtualFence';
import longitude                 from './virtualFence';
import latitudeDelta             from './virtualFence';
import longitudeDelta            from './virtualFence';
import markers                   from './virtualFence';

export default combineReducers({
  nav,
  latitude,
  longitude,
  latitudeDelta,
  longitudeDelta,
  markers,
  virtualFence,
});

这是正确的版本:

import { combineReducers }           from 'redux';
import nav                           from './nav';
import virtualFence                  from './virtualFence';

export default combineReducers({
  nav,
  virtualFence,
});

答案 1 :(得分:0)

在调用事件时是否使用了preventDefault(),可能是这种情况:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

在页面加载时使用preventdefault禁止调用方法

<Button title="Add form field" onPress={this.addFormField}></Button>
            <Button title="Delete form field" onPress={this.deleteFormField}></Button>
            <Button
              title="Submit markers"
              onPress={(argument)=>addMarkersRequestAddress(this.refs.form.getValue())}
            />

答案 2 :(得分:0)

所以你说:

  

addMarkersSuccess将触发一次,然后触发addMarkersFailure

addMarkersFailure仅在出现错误时被调用。当然,此错误包含解决问题所需的所有信息。特别是,它具有一个堆栈,不仅指示错误发生的确切位置,而且还指示完整的调用堆栈,该堆栈指示导致错误的整个调用链。

Promise有多个阶段时,每个阶段都有失败的机会。任何阶段之后的捕获都将传递错误。

所以:

Promise.resolve('This is just a string, and no error')
  .then(theString => {
    throw new Error('This is not the original promise result, but a new one: A rejection.');
  })
  .catch(err => {
    console.log('Something went wrong. Maybe it happened in the original promise.');
    console.log('Maybe it happened later. To find out, look closely at this:');
    console.log(err.stack);
  });

在您的情况下,可能是dispatch抛出了。现在,dispatch本身没有什么问题,但是当调用您的减速器时,该减速器可能在做错事并抛出错误。依次导致您的.catch回调(也称为拒绝处理程序)被调用。

由于您未包含减速器代码,因此无法指出其中的错误。但是,您应该可以通过检查错误消息和堆栈来找到它。

答案 3 :(得分:-1)

在您的date=Time.utc(2018, 07, 25, 20, 30, 45) date.class #=> Time date.in_time_zone("Madrid") #=> Wed, 25 Jul 2018 22:30:45 CEST +02:00 date.in_time_zone("Fiji") #=> Thu, 26 Jul 2018 08:30:45 +12 +12:00 date.in_time_zone("EST") #=> Wed, 25 Jul 2018 15:30:45 EST -05:00 操作中,尝试像这样返回addMarkersRequestAddress中的dispatch

.then()

也许这行得通。