Ошибка типа: this.setState не является функцией внутри файла действий Redux - PullRequest
0 голосов
/ 06 октября 2018

Я пытаюсь перенастроить приложение-клон PixaBay на Redux.Приложение извлекает фотографии, когда пользователь вводит текст для поиска.Тем не менее, он сломается, как только я наберу внутри ввода.Из того, что я исследовал, вы можете вызывать только setState в классе, поэтому я дал fetchPhotos функцию стрелки, но это не сработало.Я также попытался .bind(this), но это дало мне ошибку разбора.Может ли кто-нибудь любезно сказать мне, что я делаю не так?Вот следующие ошибки, а также мой код:

ОШИБКИ

TypeError: this.setState is not a function
fetchPhotos
src/actions/actions.js:10
   7 | 
   8 | export function fetchPhotos(e) {
   9 |   const url = `${ROOT_URL}/?key=${API_KEY}&q=${searchText}&image_type=photo`;
> 10 |   const request = this.setState({searchText: e.target.value}, () => {
  11 |     axios.get(url)
  12 |     .then(response => {
  13 |       this.setState({images: response.data.hits});

fetchPhotos
    node_modules/redux/es/redux.js:475
    Search._this.FetchPhotosHandler [as onChange]
    src/components/search/Search.js:11
       8 | class Search extends Component {
       9 | 
      10 |   FetchPhotosHandler = (e) => {
    > 11 |     this.props.fetchPhotos(e);
      12 |   }
      13 | 
      14 |   render() {

ПОИСК КОНТЕЙНЕРА

import React, { Component } from 'react';
import { fetchPhotos } from '../../actions/actions';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import TextField from 'material-ui/TextField';
import ImageResults from '../imageResults/ImageResults';

class Search extends Component {
  state = {
    searchText: '',
    images: []
  }

  FetchPhotosHandler = (e) => {
    this.props.fetchPhotos(e);
  }

  render() {
    return (
      <div>
        <TextField 
        name="searchText"
        value={this.props.searchText}
        onChange={this.FetchPhotosHandler}
        floatingLabelText="Search for photos"
        fullWidth={true} />
        <br />
        <ImageResults images={this.props.images} />
      </div>
    );
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ fetchPhotos, dispatch});
}

export default connect(null, mapDispatchToProps)(Search);

ДЕЙСТВИЕ

import axios from 'axios';
export const FETCH_PHOTOS = 'FETCH_PHOTOS';

const ROOT_URL = 'https://pixabay.com/api';
const API_KEY = '10264275-868d83de96a4d0c47db26f9e0';
const searchText = '';

export function fetchPhotos(e) {
  const url = `${ROOT_URL}/?key=${API_KEY}&q=${searchText}&image_type=photo`;
  const request = this.setState({searchText: e.target.value}, () => {
    axios.get(url)
    .then(response => {
      this.setState({images: response.data.hits});
    })
    .catch(error => {
      console.log(error)
    });
  });

  return {
    type: FETCH_PHOTOS,
    payload: request 
  };
}

REDUCER

import { FETCH_PHOTOS } from '../actions/actions';

 const initialState = {
   searchText: '',
   images: []
 }


const reducer = (state = initialState, action) => {
  switch(action.type) {
    case FETCH_PHOTOS:
      return {
        ...state,
        images: action.data.hits
      };
    default: 
      return state;
  }
}

export default reducer;

Ответы [ 3 ]

0 голосов
/ 06 октября 2018

Поскольку у вас есть fetchPhotos, экспортированные из другого модуля, и для того, чтобы сделать setState, вам нужно передать этот контекст в fetchPhotos в качестве параметра и использовать этот параметр для выполнения setState.Вот как будет доступен этот контекст

Передайте его в fetchPhotos как параметр

FetchPhotosHandler = (e) => {
    this.props.fetchPhotos(e, this);
  }

А здесь получите доступ к этому и выполните seState

export function fetchPhotos(e, this) {
  const url = `${ROOT_URL}/?key=${API_KEY}&q=${searchText}&image_type=photo`;
  const request = this.setState({searchText: e.target.value}, () => {
    axios.get(url)
    .then(response => {
      this.setState({images: response.data.hits});
    })
    .catch(error => {
      console.log(error)
    });
  });

  return {
    type: FETCH_PHOTOS,
    payload: request 
  };
}
0 голосов
/ 07 октября 2018

Вы должны избегать попыток использовать 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);

Я создал пример , чтобы показать эту функциональность в действиина базовом уровне.

Надеюсь, это поможет!

0 голосов
/ 06 октября 2018

Вы можете привязать экземпляр класса компонента к вашему действию, и оно должно работать.

FetchPhotosHandler = (e) => {
    this.props.fetchPhotos.bind(this)(e);
}
...