Запросы React [вывод данных из запроса по клику]

Всем привет.
Возникла следующая проблема. Хочу сделать вывод данных с jsonplaceholder, а именно вывод фото с помощью React. Но чтобы данные выводились не сразу, а по нажатию на кнопку. Сделал код, все выводится, но как привязать к кнопке не могу понять. Без кнопки все работает - с кнопкой, ошибка.

Подскажите, куда копать или где посмотреть подобную инфу

import { PHOTO_RESOLVE, PHOTO_REJECTED } from “…/constants”;

const setPhotoData = (data) => ({ type: PHOTO_RESOLVE, payload: data });

const setPhotoError = () => ({ type: PHOTO_REJECTED });

export const getPhoto = () => {

return (dispatch) => {

    fetch('https://jsonplaceholder.typicode.com/photos')

        .then((data) => data.json())

        .then((response) => dispatch(setPhotoData(response.slice(0, 10))))

        .catch(() => dispatch(setPhotoError()))

};

};

import React from “react”;

import { connect } from “react-redux”;

import { getPhoto } from “…/…/actions/photo”;

class Photo extends React.Component {

componentDidMount() {

    this.props.getPhoto();

}

render() {

    const {photo } = this.props;

    return (

        // <button type="button" onClick={()=> photo()}>

        <div>

                   <ul>

                        {

                            photo.map((item) => (

                                <li key={item.id}>

                                   <img src={item.url} alt={`photo-${item.id}`}/>

                                </li>

                            ))

                        }

                    </ul>

        </div>

    )

}

}

const mapStateToProps = (state) => ({

isLoading: state.photoData.isLoading,

photo: state.photoData.photo

});

const mapDispatchToProps = {

getPhoto: getPhoto

};

export const PhotoContainer = connect(mapStateToProps, mapDispatchToProps)(Photo);

import { PHOTO_RESOLVE, PHOTO_REJECTED } from “…/constants”;

const initialValues = {

photo: [],

isLoading: true,

isError: false

}

export const photoReducer = (state = initialValues, action) => {

switch(action.type) {

    case PHOTO_RESOLVE: {

        return {

            ...state,

            photo: action.payload,

            isLoading: false

        };

    }

    case PHOTO_REJECTED: {

        return {

            ...state,

            isLoading: false,

            isError: true

        }

    }

    default: {

        return state;

    }

}

};

Вроде все правильно, не хватает внутреннего состояния.

Вы при монтировании компонента отправляете запрос на получение данных this.props.getPhoto();

после чего ваша кнопка управляет внутренним состоянием компонента для отрисовки блока (кнопка не нажимается пока нет данных, ну или кнопка не должна отрисовываться или быть активной):
<button type="button" onClick={()=> { photo && this.setState({ showPhoto: true } ) }}>
или так

showPhoto () {
  this.props.isLoading && this.setState({ showPhoto: true }
}
<button type="button" onClick={showPhoto}>

и когда состояние меняется блок покажется:
{ this.state.showPhoto && ( photo.map((item) => (

P.S.: возможно в вашем проекте вместо внутреннего стейта, все надо через редакс прокидывать, тогда надо создать все экшены редьюсеры для такого состояния (по аналогии с isLoading).

P.S.S.: надеюсь это старый легаси проект, а не новый или учебный, так как на '20 лучше использовать https://redux-toolkit.js.org (избавит от болерплейта) и функционалые компоненты на хуках.

Запутался совсем теперь
Когда пытаюсь подключить кнопку, выдает ошибку

Как пытаетесь подключить, какую ошибку выдает?

С ошибкой разобрался, немного не туда пытался подключить кнопку.
Но проблему не решил
Вот код, картинки выводятся, равно как и кнопка сразу. То есть контент появляется не после нажатия на нее. Скорей всего что-то делаю не так…

return (

        <div>

        <button type="button" onClick={()=> { photo && this.setState({ showPhoto: true } ) }}>Button</button>

                   <ul>

                        {

                            photo.map((item) => (

                                <li key={item.id}>

                                   <img src={item.url} alt={`photo-${item.id}`}/>

                                </li>

                            ))

                        }

                    </ul>

        </div>

    )

В моем ответе выше я указывал конструкцию проверки:

и когда состояние меняется блок покажется:
{ this.state.showPhoto && ( photo.map((item) => (

собственно должна существовать проверка показывать блок или нет, и это проверка значения вашего внутреннего стейта-нажатости кнопки (если true показываем блок, по умолчанию там false и блок не отрисовывается):

{ this.state.showPhoto && ( 
  photo.map((item) => (
    ...
  )
)}`

можно и через redux реализовать, создать свое значение в сторе, типа visiblePhotos: и при нажатии кнопки, дергать экшен showPhotos(), который в редьюсере данные скопирует из photo = visiblePhotos :-) тогда конструкция тоже будет работать визуально так же visiblePhotos.map((item) => ( так как map по пустому массиву ничего не выведет, а когда массив наполнится, появится и список. Правда накладные расходы не оправдают решение с обычной булевой переменной.

Насколько я вас понял, должно получится что-то наподобие этого . но все равно не работает

class Photo extends React.Component {

componentDidMount() {

    this.props.getPhoto();

}

render() {

    const {photo } = this.props;

    return (

        <div>

        <button type="button" onClick={()=> { photo && this.setState({ showPhoto: true } ) }}>Button</button>

 {

   { this.state.showPhoto && ( photo.map((item) => (

    <ul>

        <li key={item.id}>

           <img src={item.url} alt={`photo-${item.id}`}/>

        </li>

    </ul>

    )))

   }

})

}

}

const mapStateToProps = (state) => ({

isLoading: state.photoData.isLoading,

photo: state.photoData.photo

});

const mapDispatchToProps = {

getPhoto: getPhoto

};

export const PhotoContainer = connect(mapStateToProps, mapDispatchToProps)(Photo);

Что не работает? Какую ошибку в Dev Tools консоле выводит? Вы инициировали внутренний стейт у вашего классового компонента?

constructor(props) {
  super(props);
  this.state = {showPhoto: false};
}

к тому же вы сделали не валидную конструкцию:

<button>Button</button>
{ — открывается выражение и следом за ним открываете еще раз
   { this.state.showPhoto && ( photo.map((item) => (
    <ul> — пропал атрибут ключа, на что уже ругался бы сборщик, при том ul попадает в отрисовку массива, не корректно.
        <li key={item.id}>

Упростите конструкцию для понимания:

render() {
  const { photo } = this.props;
  if (!photo) {
    return 'Loading...';
  }

  if (this.state.showPhoto) {
    return (
        <ul>
          {photo.map(item => (
            <li key={item.id}>
               <img src={item.url} alt={`photo-${item.id}`}/>
            </li>
          )}
        </ul>
    )
  }

  return <button type="button" onClick={()=> { this.setState({ showPhoto: true } ) }}>Button</button>;
}

Теперь ошибка на валидность, ругается на скобки. Хотя вроде бы все верно.
и не совсем ясно, как у вас в коде будет отрабатывать второй return ,который на кнопке? он же вроде бы никогда не сработает

вот, что у меня в итоге вышло

class Photo extends React.Component {

constructor(props) {

super(props);

this.state = {getPhoto: false};

}

componentDidMount() {

this.props.getPhoto();

}

render() {

const { photo } = this.props;

if (!photo) {

return 'Loading...';

}

if (this.state.showPhoto) {

return (

    <ul>

      {photo.map(item => (

        <li key={item.id}>

           <img src={item.url} alt={`photo-${item.id}`}/>

        </li>

      )}

    </ul>

)

}

return <button type=“button” onClick={()=> { this.setState({ showPhoto: true } ) }}>Button;

}

const mapStateToProps = (state) => ({

isLoading: state.photoData.isLoading,

photo: state.photoData.photo

});

const mapDispatchToProps = {

getPhoto: getPhoto

};

export const PhotoContainer = connect(mapStateToProps, mapDispatchToProps)(Photo);

В чем у вас вызвало сложность разобраться в расстановкой скобок имея IDE под рукой?

Сводка
class Photo extends React.Component {
  constructor(props) {
    super(props);
    this.state = { getPhoto: false };
  }

  componentDidMount() {
    this.props.getPhoto();
  }

  render() {
    const { photo } = this.props;

    if (!photo.length) {
      return 'Loading...';
    }

    if (this.state.showPhoto) {
      return (
        <ul>
          {photo.map(item => (
            <li key={item.id}>
              <img src={item.url} alt={`photo-${item.id}`} />
            </li>
          ))}
        </ul>
      )
    }

    return (
      <button
        type="button"
        onClick={() => {
          this.setState({ showPhoto: true })
        }}>
        Button
      </button>
    )
  }
}

const mapStateToProps = (state) => ({
  isLoading: state.photoData.isLoading,
  photo: state.photoData.photo
})

const mapDispatchToProps = {
  getPhoto: getPhoto
}

export const PhotoContainer = connect(
  mapStateToProps,
  mapDispatchToProps,
)(Photo)

На кнопке третий return а второй на списке фотограй, а первый на блоке если отсутствуют фото.

При отрисовке photo пустое, там кстати ошибка !photo.length надо размерность массива проверить (или !isLoading). если фого нет отрисовывается Loading, если появляется, прийдся с сервера, то проверяется надо ли показывать список фото (статусом нашей кнопки определяется) if (this.state.showPhoto) { если показывать список не надо (а это значение по умолчанию) значит показывается кнопка в последнем return.

2 лайка

Спасибо огромное, все заработало, буду теперь разбираться что откуда берется =)