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 специально, потому что, если я addMarkersSuccess вызовы 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/info/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;
}

Обратите внимание, что я действительно не знаю, что делает этот файл. Я начал приложение, используя реактивный-native-шаблон, чтобы этот файл был взят оттуда. Если там нужно внести изменения, было бы очень приятно, если бы вы могли подробно описать, что именно делают эти изменения.

РЕДАКТИРОВАТЬ 1: Когда это сообщение было изначально написано, все отправления после первых ошибок. После некоторых дальнейших работ в других частях приложения, дополнительные записи всех журналов теперь успешны. Однако основной вопрос (причина множественных стрельб) остается.

EDIT 2: добавлена упаковка контейнера вокруг компонента.

Ответы

Ответ 1

Причина моей проблемы оказалась в файле, где я называю вспомогательную функцию combReducers. Я не подозревал, что этот файл имеет какое-либо отношение к проблеме, поэтому я не разместил его. Для компонентов с несколькими ключами в исходном объекте состояния я неправильно думал, что мне нужно сделать импорт для каждого ключа, когда на самом деле мне нужен был единственный импорт для каждого файла редуктора. Я импортировал шесть переменных из редуктора 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,
});

Ответ 2

вы используете 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())}
            />

Ответ 3

Итак, вы заявляете:

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 callback (иначе называемому обработчиком отклонения).

Поскольку вы не указали свой код редуктора, я не могу указать на ошибку. Однако вы можете найти его, изучив сообщение об ошибке и стек.

Ответ 4

В вашем действии addMarkersRequestAddress попытайтесь вернуть dispatch в .then() как:

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

Возможно, это сработает.