Как преобразовать приложение React в приложение React-Redux? - PullRequest
0 голосов
/ 06 марта 2019

Я создал очень простое приложение только с React, и теперь я хочу изменить его, чтобы в нем можно было использовать Redux. (Я знаю, что Redux здесь не нужен, но я просто пытаюсь изучить Redux).

SearchBar.js - только реакция

import React, { Component } from "react";

class SearchBar extends Component {
  state = {
    inputValue: ""
  };

  handleFormSubmit = e => {
    e.preventDefault();
    this.props.onSubmit(this.state.inputValue);
  };

  render() {
    return (
      <div className="header">
        <h1>Search for images on Unsplash</h1>
        <form onSubmit={this.handleFormSubmit} className="ui form">
          <input
            type="text"
            placeholder="Type here to search for images"
            value={this.state.inputValue}
            onChange={e => this.setState({ inputValue: e.target.value })}
          />
        </form>
      </div>
    );
  }
}

export default SearchBar;

App.js - только реакция

import React, { Component } from "react";
import axios from "axios";
import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";

class App extends Component {
  state = {
    images: []
  };

  onSearchSubmit = async inputValue => {
    const API_KEY =
      "<MY API KEY FOR UNSPLASH>";

    const response = await axios.get(
      `https://api.unsplash.com/search/photos?page=1&query=${inputValue}&client_id=${API_KEY}`
    );

    this.setState({ images: response.data.results });
  };

  render() {
    return (
      <>
        <SearchBar onSubmit={this.onSearchSubmit} />
        <div>
          <ImageList images={this.state.images} />
        </div>
      </>
    );
  }
}

export default App;

Использование Redux

Я поставил версию-редукса на codeSandBox . Конечно, это еще не работает.

Вот мои изменения:

App.js с редуксом

import React, { Component } from "react";
import { Provider } from "react-redux";
import store from "./store";

import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";
import "./app.scss";

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <SearchBar onSubmit={this.onSearchSubmit} />
        <div>
          <ImageList images={this.state.images} />
        </div>
      </Provider>
    );
  }
}

export default App;

store.js

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";

const initialState = {};

const middleware = [thunk];

const store = createStore(
  rootReducer,
  initialState,
  compose(
    applyMiddleware(...middleware),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
);

export default store;

fetchAction.js

import axios from "axios";

export const FETCH_DATA = "fetch_data";

// Getting all images
export const getImages = inputValue => async dispatch => {
  const API_KEY =
    "<MY API KEY FOR UNSPLASH>";
  const res = await axios.get(
    `https://api.unsplash.com/search/photos?page=1&query=${inputValue}&client_id=${API_KEY}`
  );
  console.log(res.data.results);

  dispatch({
    type: FETCH_DATA,
    payload: res.data.results
  });
};

index.js внутри папки редукторов

import { combineReducers } from "redux";
import fetchReducer from "./fetchReducer";

export default combineReducers({
  images: fetchReducer
});

fetchReducer.js

import { FETCH_DATA } from "../actions/fetchAction";

const initialState = {};

export default function(state = initialState, action) {
  switch (action.type) {
    case FETCH_DATA:
      return {
        ...state
      };

    default:
      return state;
  }
}

Но у меня есть два вопроса:

  1. Где мне использовать connect? в App.js или в SearchBar.js?
  2. Если я добавлю следующее в свой компонент, где я использую connect:

    const mapStateToProps = state => ({ изображений: });

    экспорт по умолчанию подключиться ( mapStateToProps, {getImages} ) (SearchBar);

Каким будет значение изображений внутри mapStateToProps?

Я поставил версию-редукса на codeSandBox . Конечно, это еще не работает.

Ответы [ 3 ]

0 голосов
/ 06 марта 2019

1) вы должны использовать connect в SearchBar 2) Это зависит от места, где вы вызываете вызов API для получения изображений.Попробуйте что-то вроде этого, вам придется вызывать this.props.getImages(..) внутри компонента Gallery, скорее всего, когда вы набираете что-то

import React, { Component } from "react";
import { connect } from "react-redux";
import { getImages } from "../actions/fetchAction";
import Masonry from "react-masonry-component";

const masonryOptions = {
  transitionDuration: 1
};

const imagesLoadedOptions = { background: ".my-bg-image-el" };

class Gallery extends Component {
  childElements = () => {
    if (this.props.images) {
      return this.props.images.map(item => {
        return (
          <li className="masonry_item" key={item.id}>
            <img
              src={item.urls.regular}
              alt={item.description}
              className="masonry_item_img"
            />
          </li>
        );
      });
    }
    return <div>no images</div>;
  };

  render() {
    console.log(this.props);
    // map method generates a new Array

    return (
      <Masonry
        className={"masonry"} // default ''
        elementType={"ul"} // default 'div'
        options={masonryOptions} // default {}
        disableImagesLoaded={false} // default false
        updateOnEachImageLoad={false} // default false and works only if disableImagesLoaded is false
        imagesLoadedOptions={imagesLoadedOptions} // default {}
      >
        {this.childElements()}
      </Masonry>
    );
  }
}

let mapStateToProps = (state, props) => {
  return {
    images: state.images.images
  };
};

let mapDispatchToProps = dispatch => {
  return {
    getImages: data => {
      dispatch(getImages(data));
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Gallery);
0 голосов
/ 08 марта 2019

Так что для вашего store.js вместо:

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";

const initialState = {};

const middleware = [thunk];

const store = createStore(
  rootReducer,
  initialState,
  compose(
    applyMiddleware(...middleware),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
);

export default store;

попытайтесь:

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk';

import App from "./components/App";
import reducers from "./reducers";

const store = createStore(reducers, applyMiddleware(thunk));

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector("#root")
);

Затем в вашем файле App.js вместо:

import React, { Component } from "react";
import { Provider } from "react-redux";
import store from "./store";
import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";
import "./app.scss";

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <SearchBar onSubmit={this.onSearchSubmit} />
        <div>
          <ImageList images={this.state.images} />
        </div>
      </Provider>
    );
  }
}

export default App;

try:

import React, { Component } from "react";
import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";
import "./app.scss";

class App extends Component {
  render() {
    return (
      <div>
        <SearchBar onSubmit={this.onSearchSubmit} />
        <div>
          <ImageList images={this.state.images} />
        </div>
      </div>
    );
  }
}

export default App;

Для вашего запроса Axios вместо создания всего этого кода внутри fetchActions.js создайте структуру папок / файлов apis/unsplash.js:

import axios from 'axios';

export default axios.create({
    baseURL: 'https://api.unsplash.com'
});

Тогда внутри вашего fetchActions.js:

export const FETCH_DATA = "fetch_data";

// Getting all images
export const getImages = inputValue => async dispatch => {
  const API_KEY =
    "<MY API KEY FOR UNSPLASH>";
  const res = await unsplash.get(
    `/search/photos?page=1&query=${inputValue}&client_id=${API_KEY}`
  );
  console.log(res.data.results);

  dispatch({
    type: "FETCH_DATA",
    payload: res.data.results
  });
};

Ваш combineReducers выглядит хорошо.Ваш fetchReducer.js, я часто вижу это в коммерческих приложениях: const initialState = {};

Это на самом деле не нужно, вырежьте этого initialState посредника и просто:

import { FETCH_DATA } from "../actions/fetchAction";

export default function(state = {}, action) {
  switch (action.type) {
    case "FETCH_DATA":
      return {
        ...state
      };

    default:
      return state;
  }
}

Хорошо, чисто,элегантный.Теперь, где использовать Connect?в App или SearchBar компонент?Давайте спросим себя, какова цель функции connect?

Давайте посмотрим, мы создали наш магазин Redux, а затем передали его в Provider Почему мы это сделали?Ах, да, так что любой компонент внутри нашего приложения может получить доступ к хранилищу Redux через тег Provider.

Другими словами, получить доступ к некоторым данным.Так какой компонент должен получить доступ к некоторым данным?App?Нет, не совсем, он является родительским компонентом в иерархии и просто хорошо держит все остальные компоненты, верно?

Но наши SearchBar, мы собираемся получить доступ к некоторым данным через эту панель поиска,верно?

Теперь компонент SearchBar может быть не единственным компонентом, где вам может понадобиться функция connect, но я бы начал с импорта:

import { connect } from 'react-redux';

вверху SearchBar, затем внизу SearchBar Я бы реализовал:

export default connect()(SearchBar)

Что в мире ?!Почему у нас есть второй набор скобок вокруг SearchBar?!

Поскольку я пытался объяснить бывшему студенту, который пожаловался администраторам, что я некомпетентен, чтобы предложить ту самую строку кода, которую вы видите вышеэта строка кода ничем не отличается от этого:

function connect() {
  return function() {
    return 'howdy!';
  }
}
connect();

О боже, функция, которая возвращает функцию, но подождите, она ничего не печатает!

Но еслиЯ сразу добавляю вторые скобки:

function connect() {
      return function() {
        return 'howdy!';
      }
    }
    connect()();

Я получаю отпечаток из howdy!

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

Что касается вашего mapStateToProps, то здесь чего-то не хватает, я думаю, это должно выглядеть так:

const mapStateToProps = state => {
  return { images: state.images };
};
0 голосов
/ 06 марта 2019

Рекомендуемый стандарт / общий процесс заключается в настройке подключенной логики в отдельном файле контейнера.Это будет содержать функцию подключения, а также любые mapStateToProps, mapDispatchToProps и т. Д.

Ваш контейнер может выглядеть следующим образом:

import SearchBar from "./components/SearchBar"

const mapStateToProps = state => ({ 
  images: images(state)
});

export default connect(mapStateToProps)(SearchBar)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...