admin管理员组

文章数量:1277901

I'm making a React/Redux app. In one of my actions, dispatch is firing 6-8 times when called for no apparent reason. See addMarkersRequestAddress below in the action file for my ponent:

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));
        });

  }

}

When this code runs, addMarkersSuccess will fire 6-8 times. It is somehow related to dispatch specifically, because if I remove the dispatch calls and leave only the console logs, addMarkersSuccess fires once as expected and that's it. It also seems unrelated to fetch or asynchronicity since an identical oute occurs if fetch is removed and the same thing is tried in the main body of the function.

Here is the container wrapping around the ponent (since I've narrowed it down to an issue with how dispatch is called, as without dispatch other parts of the action only fire once, maybe there is an issue with how dispatch is set up here?):

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 '../ponents/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>
    );
  }
}

Here is where the action is called in the ponent itself:

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>
    );
  }

While not answering my question, some other answers here and elsewhere seemed to hint that the resolution may have something to do with my configureStore.js file, so here it is:

/* eslint global-require: 0 */

import { Platform } from 'react-native';
import { createStore, applyMiddleware, pose } 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 poseEnhancers = pose;
if (__DEV__) {
  // Use it if Remote debugging with RNDebugger, otherwise use remote-redux-devtools
  /* eslint-disable no-underscore-dangle */
  poseEnhancers = (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||
    require('remote-redux-devtools')poseWithDevTools)({
    name: Platform.OS,
    ...require('../package.json').remotedev,
    actionCreators,
  });
  /* eslint-enable no-underscore-dangle */
}

const enhancer = poseEnhancers(applyMiddleware(thunk));

// I think the problem with multiple dispatches may be in here
// See 
export default function configureStore(initialState) {
  const store = createStore(reducer, initialState, enhancer);
  if (module.hot) {
    module.hot.accept(() => {
      store.replaceReducer(require('./reducers').default);
    });
  }
  return store;
}

Please note that I don't really know what this file is doing. I began the app using react-native-boilerplate so this file is taken from there. If changes need to be made there, it would be super appreciated if you can detail what exactly those changes do.

EDIT 1: When this post was originally written, all dispatches after the first threw errors. After some further work in other parts of the application, the additional firings all log successful now. However, the essential question (the cause of the multiple firings) remains.

EDIT 2: Added the container wrapping around the ponent.

I'm making a React/Redux app. In one of my actions, dispatch is firing 6-8 times when called for no apparent reason. See addMarkersRequestAddress below in the action file for my ponent:

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));
        });

  }

}

When this code runs, addMarkersSuccess will fire 6-8 times. It is somehow related to dispatch specifically, because if I remove the dispatch calls and leave only the console logs, addMarkersSuccess fires once as expected and that's it. It also seems unrelated to fetch or asynchronicity since an identical oute occurs if fetch is removed and the same thing is tried in the main body of the function.

Here is the container wrapping around the ponent (since I've narrowed it down to an issue with how dispatch is called, as without dispatch other parts of the action only fire once, maybe there is an issue with how dispatch is set up here?):

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 '../ponents/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>
    );
  }
}

Here is where the action is called in the ponent itself:

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>
    );
  }

While not answering my question, some other answers here and elsewhere seemed to hint that the resolution may have something to do with my configureStore.js file, so here it is:

/* eslint global-require: 0 */

import { Platform } from 'react-native';
import { createStore, applyMiddleware, pose } 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 poseEnhancers = pose;
if (__DEV__) {
  // Use it if Remote debugging with RNDebugger, otherwise use remote-redux-devtools
  /* eslint-disable no-underscore-dangle */
  poseEnhancers = (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||
    require('remote-redux-devtools').poseWithDevTools)({
    name: Platform.OS,
    ...require('../package.json').remotedev,
    actionCreators,
  });
  /* eslint-enable no-underscore-dangle */
}

const enhancer = poseEnhancers(applyMiddleware(thunk));

// I think the problem with multiple dispatches may be in here
// See https://stackoverflow./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;
}

Please note that I don't really know what this file is doing. I began the app using react-native-boilerplate so this file is taken from there. If changes need to be made there, it would be super appreciated if you can detail what exactly those changes do.

EDIT 1: When this post was originally written, all dispatches after the first threw errors. After some further work in other parts of the application, the additional firings all log successful now. However, the essential question (the cause of the multiple firings) remains.

EDIT 2: Added the container wrapping around the ponent.

Share Improve this question edited Jul 27, 2018 at 13:10 223seneca asked Jul 23, 2018 at 13:23 223seneca223seneca 1,1764 gold badges22 silver badges49 bronze badges 6
  • Can you show how you use these actions in your ponent? Please show a minimal reproducible example. Note the word "plete". – Code-Apprentice Commented Jul 23, 2018 at 14:22
  • I've added the render function from the ponent where this action is called. – 223seneca Commented Jul 23, 2018 at 14:24
  • Please read the link I gave in my previous ment. – Code-Apprentice Commented Jul 23, 2018 at 14:25
  • Where are you executing the addMarkersRequestCoordinates function? Your reducer action seems ok. – Marco Mendão Commented Jul 23, 2018 at 14:25
  • I did read the link. What is the issue? – 223seneca Commented Jul 23, 2018 at 14:26
 |  Show 1 more ment

4 Answers 4

Reset to default 3

The cause of my problem turned out to be in the file where I call the bineReducers helper function. I did not suspect this file had anything to do with the problem, so I had not posted it. For ponents with multiple keys in the initial state object, I incorrectly thought I had to do an import for each key, when in fact I needed a single import for each reducer file. I imported six variables from the virtualFence reducer, and each one caused dispatch to fire.

This is the incorrect version:

import { bineReducers }       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 bineReducers({
  nav,
  latitude,
  longitude,
  latitudeDelta,
  longitudeDelta,
  markers,
  virtualFence,
});

And this is the correct version:

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

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

are you using preventDefault() when calling event this might be the case:

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

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

use preventdefault to disallow to call method when page is loading

<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())}
            />

So you state:

addMarkersSuccess will fire once, followed by several firings of addMarkersFailure

addMarkersFailure only gets called when there is an error. This error, of course, contains all the information you need to solve the problem. In particular, it has a stack that indicates not only the exact place the error was fired, but a plete call stack that indicates the entire call chain leading up to the error.

When a Promise has several stages, each stage has an opportunity to fail. A catch following any of the stages will be passed the error.

So:

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);
  });

In your case, it's probably dispatch that throws. Now, there's nothing wrong with dispatch itself, but when it goes to call your reducer, the reducer is probably doing something wrong and throwing an error. This in turn leads your .catch callback (aka the rejection handler) to be called.

Since you did not include your reducer code I can't point out the error in it. However, you should be able to find it by examining the error message and stack.

In your addMarkersRequestAddress action, try to return the dispatch in .then() like:

.then((response) => {
      dispatch(addMarkersSuccess(response));
    }).catch((error) => {
      dispatch(addMarkersFailure(error));
    });

Maybe this will work.

本文标签: javascriptReactNativeRedux dispatch firing multiple times in actionStack Overflow