Вы должны избегать попыток использовать setState()
в своих действиях, поскольку они полностью противоречат Redux.setState()
предназначен для управления локальным из React.Component
.Поскольку вы пытаетесь использовать Redux, вам следует вместо этого отправлять действия создателям ваших действий, которые обновляют хранилище с помощью ваших редукторов и, наконец, сопоставляют значения хранилища с реквизитами вашего компонента через connect()
.Ниже приведен пример реструктуризации вашего кода, подобный примеру Async Redux.
Вместо попытки вызвать setState()
в действии, вместо этого отправляется действие, содержащее полезную нагрузку изображения.Компонент Search
использует mapStateToProps (1-й аргумент connect ()) для сопоставления свойств хранилища, таких как массив изображений, с реквизитами компонента.Эти реквизиты используются для визуализации списка данных.Это полностью исключает необходимость иметь свойство images
local state для Search
, так как значения извлекаются из хранилища, поскольку изменения происходят через действия / редукторы.В этом примере для обработки асинхронных действий используется промежуточное программное обеспечение redux-thunk , но существует множество других вариантов, которые можно рассмотреть.
store:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const middleware = [ thunk ];
const store = createStore(
rootReducer,
applyMiddleware(...middleware)
);
export default store;
actions:
export const FETCH_PHOTOS = 'FETCH_PHOTOS';
export const RECEIVE_PHOTOS = 'RECEIVE_PHOTOS';
// fake data
const testPhotos = [
{ id: 1, src: 'https://placehold.it/250' },
{ id: 2, src: 'https://placehold.it/250' }
];
// fake API call as promise
const getTestPhotos = () => {
return new Promise((resolve) => {
setTimeout(() => {
return resolve(testPhotos);
}, 500);
});
}
const fetchPhotos = (searchText) => ({
type: FETCH_PHOTOS
});
const receivePhotos = (photos) => ({
type: RECEIVE_PHOTOS,
data: {
hits: photos
}
});
export const searchPhotos = (searchText) => dispatch => {
// dispatch an action to handle loading/waiting for API response
dispatch(fetchPhotos(searchText));
// dispatch another action with payload within then()
return getTestPhotos()
.then(photos => dispatch(receivePhotos(photos)));
}
редуктор:
import { FETCH_PHOTOS, RECEIVE_PHOTOS } from '../actions';
const initialState = {
loading: false,
searchText: '',
images: []
}
const photos = (state = initialState, action) => {
switch(action.type) {
case FETCH_PHOTOS:
return {
...state,
loading: true
};
case RECEIVE_PHOTOS:
return {
...state,
loading: false,
images: action.data.hits
};
default:
return state;
}
}
export default photos;
Поиск:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchPhotos } from './actions';
class Search extends Component {
constructor(props) {
super(props);
this.state = {
searchText: ''
};
this.fetchPhotosHandler = this.fetchPhotosHandler.bind(this);
}
fetchPhotosHandler(e) {
const { value: searchText } = e.target;
this.setState({ ...this.state, searchText }, () => {
this.props.dispatch(searchPhotos(e));
})
}
render() {
const { loading, images } = this.props;
return (
<div>
<h1>Search</h1>
<div>
<label htmlFor="search">Search:</label>
<input name="search" id="search" type="text" value={this.state.searchText} onChange={this.fetchPhotosHandler} />
</div>
{loading ? (
<div>Loading...</div>
) : (
<ul>
{images.map(image => <li key={image.id}>{image.src}</li>)}
</ul>
)}
</div>
);
}
}
const mapStateToProps = ({ photos: { loading, images } }) => ({ loading, images });
export default connect(mapStateToProps)(Search);
Я создал пример , чтобы показать эту функциональность в действиина базовом уровне.
Надеюсь, это поможет!