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));
});
Возможно, это сработает.