简体   繁体   English

React-Native / Redux调度多次激活

[英]React-Native/Redux dispatch firing multiple times in action

I'm making a React/Redux app. 我正在制作一个React / Redux应用程序。 In one of my actions, dispatch is firing 6-8 times when called for no apparent reason. 在我的一个行动中,当没有明显理由要求时, dispatch发射6-8次。 See addMarkersRequestAddress below in the action file for my component: 请参阅下面的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));
        });

  }

}

When this code runs, addMarkersSuccess will fire 6-8 times. 当此代码运行时, addMarkersSuccess将触发6-8次。 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. 它与dispatch具体相关,因为如果我删除dispatch调用并只保留控制台日志,则addMarkersSuccess会按预期触发一次,就是这样。 It also seems unrelated to fetch or asynchronicity since an identical outcome occurs if fetch is removed and the same thing is tried in the main body of the function. 它似乎与fetch或异步性无关,因为如果删除了fetch并且在函数的主体中尝试了同样的事情,则会出现相同的结果。

Here is the container wrapping around the component (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?): 这是围绕组件的容器(因为我已经将其缩小到调用如何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>
    );
  }
}

Here is where the action is called in the component 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: 虽然没有回答我的问题,但其他地方和其他地方的其他答案似乎暗示解决方案可能与我的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;
}

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. 我使用react-native-boilerplate启动了应用程序,因此这个文件是从那里获取的。 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. 编辑1:当这篇文章最初编写时,第一次发送后的所有调度都会引发错误。 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 component. 编辑2:添加了包裹在组件周围的容器。

The cause of my problem turned out to be in the file where I call the combineReducers helper function. 我的问题的原因结果是在我调用combineReducers辅助函数的文件中。 I did not suspect this file had anything to do with the problem, so I had not posted it. 我没有怀疑这个文件与问题有什么关系,所以我没有发布它。 For components 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. 对于初始状态对象中具有多个键的组件,我错误地认为我必须为每个键执行导入,而实际上我需要为每个reducer文件进行一次导入。 I imported six variables from the virtualFence reducer, and each one caused dispatch to fire. 我从virtualFence reducer中导入了六个变量,每个变量都会导致dispatch

This is the incorrect version: 这是不正确的版本:

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

And this is the correct version: 这是正确的版本:

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

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

are you using preventDefault() when calling event this might be the case: 你在调用事件时使用的是preventDefault(),可能就是这种情况:

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

So you state: 所以你陈述:

addMarkersSuccess will fire once, followed by several firings of addMarkersFailure addMarkersSuccess将触发一次,然后addMarkersFailure几次addMarkersFailure

addMarkersFailure only gets called when there is an error. 只有在出现错误时才会调用addMarkersFailure 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 complete 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. Promise有几个阶段时,每个阶段都有失败的机会。 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. 在你的情况下,它可能是抛出的dispatch 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. 现在, dispatch本身并没有什么问题,但是当它调用你的 reducer时,reducer可能做错了什么并抛出错误。 This in turn leads your .catch callback (aka the rejection handler) to be called. 这反过来导致你的.catch回调(也称为拒绝处理程序)被调用。

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: addMarkersRequestAddress操作中,尝试返回addMarkersRequestAddress .then()dispatch .then()如:

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

Maybe this will work. 也许这会奏效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM