简体   繁体   English

如何将React应用转换为React-Redux应用?

[英]How to convert React app to React-Redux app?

I created a very simple app with only React and now I want to change it so, I can use Redux in it. 我仅使用React创建了一个非常简单的应用程序,现在我想对其进行更改,因此可以在其中使用Redux。 (I know Redux is not needed here, but I'm just trying to learn Redux). (我知道这里不需要Redux,但我只是想学习Redux)。

SearchBar.js - Only React SearchBar.js-仅React

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 - Only React App.js-仅React

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;

Using Redux 使用Redux

I put the redux-version on codeSandBox . 我将redux-version放在了codeSandBox上 Of course it's not working yet. 当然还不能用。

Here are my changes so far: 到目前为止,这是我的更改:

App.js with redux 带有Redux的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 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 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 inside reducers folder reducers文件夹中的index.js

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

export default combineReducers({
  images: fetchReducer
});

fetchReducer.js 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;
  }
}

But, I have two questions: 但是,我有两个问题:

  1. Where should I use connect? 我应该在哪里使用连接? in App.js or in SearchBar.js? 在App.js或SearchBar.js中?
  2. If I add the following to my component, where I use connect: 如果将以下内容添加到我的组件中,则在此处使用connect:

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

    export default connect( mapStateToProps, { getImages } )(SearchBar); 导出默认的connect(mapStateToProps,{getImages})(SearchBar);

What would be the value of images inside mapStateToProps? mapStateToProps中的图像的值是多少?

I put the redux-version on codeSandBox . 我将redux-version放在了codeSandBox上 Of course it's not working yet. 当然还不能用。

The advised standard/ a common process is to set your connected logic inside a separate container file. 建议的标准/常见过程是在单独的容器文件中设置连接的逻辑。 This will contain you connect function as well as any mapStateToProps, mapDispatchToProps etc 这将包含您的连接函数以及任何mapStateToProps,mapDispatchToProps等

Your container could then look like this: 然后,您的容器可能如下所示:

import SearchBar from "./components/SearchBar"

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

export default connect(mapStateToProps)(SearchBar)

1) you should use connect in SearchBar 2) This depends on the place where you call the API call to fetch images. 1)您应该在SearchBar中使用connect 2)这取决于您调用API调用以获取图像的位置。 Try something like this you will have to call this.props.getImages(..) inside your Gallery component, most probably when you type in something 尝试类似这样的事情,您必须在Gallery组件内部调用this.props.getImages(..) ,最有可能是在您键入某些内容时

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);

So for your store.js , instead of: 因此,对于您的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;

try: 尝试:

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")
);

Then in your App.js file, instead of: 然后在您的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;

For your Axios request, instead of throwing all that code inside of fetchActions.js , create a folder/file structure apis/unsplash.js : 对于您的Axios请求,不要将所有代码都fetchActions.js内, fetchActions.js创建一个文件夹/文件结构apis/unsplash.js

import axios from 'axios';

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

Then inside your fetchActions.js : 然后在您的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
  });
};

Your combineReducers is looking good. 您的combineReducers看起来不错。 Your fetchReducer.js , I see this often in commercial applications: const initialState = {}; 您的fetchReducer.js ,我经常在商业应用程序中看到: const initialState = {};

It's not really necessary, cut that initialState middleman out and just: 并没有必要,切掉initialState中间人,只需:

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

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

    default:
      return state;
  }
}

Nice, clean, elegant. 尼斯,干净,优雅。 Now, where to use connect? 现在,在哪里使用连接? in App or SearchBar component? AppSearchBar组件中? Let's ask ourselves, what is the purpose of the connect function? 让我们问问自己, connect功能的目的是什么?

Let's see, we created our Redux store and then passed it off to the Provider Why did we do that? 让我们看看,我们创建了Redux存储,然后将其传递给Provider为什么我们要这样做? Ahh, yes, so that any component inside our application can gain access to the Redux store through the Provider tag. 嗯,是的,这样我们应用程序内的任何组件都可以通过Provider标签访问Redux存储。

In other words access some data. 换句话说,访问一些数据。 So what component needs to access some data? 那么什么组件需要访问一些数据? App ? App Nah, not really, its the parent component in the hierarchy and just keeps all the other components wired up nicely, right? 不,不是,它是层次结构中的父组件,只是使所有其他组件保持良好的连线,对吗?

But our SearchBar , we are going to be accessing some data via that search bar, right? 但是我们的SearchBar ,我们将通过该搜索栏访问一些数据,对吗?

Now, the SearchBar component may not be the only component where you might need the connect function but I would start there by importing: 现在, SearchBar组件可能不是您可能需要connect函数的唯一组件,但是我将从导入开始:

import { connect } from 'react-redux';

at the top of SearchBar , then at the bottom of SearchBar I would implement: SearchBar的顶部,然后在SearchBar的底部,我将实现:

export default connect()(SearchBar)

What in the world?! 到底是什么? Why do we have a second set of parentheses around SearchBar ?! 为什么在SearchBar周围还有第二个括号?

As I had tried to explain to a former student who complained to the administrators that I was incompetent for offering that very line of code you see above, that line of code is no different than doing this: 正如我曾试图向一位以前的学生解释的那样,该学生向管理员抱怨我没有能力提供上面看到的那行代码,这行代码与执行此操作没有什么不同:

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

Oh my, a function that returns a function, but wait, that doesn't print out anything! 哦,我的,一个返回一个函数的函数,但是等等,它什么都不会打印出来!

But if I immediately add on a second parentheses: 但是,如果我立即加上第二个括号:

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

I get the print out of howdy! 我把打印出来的howdy!

All we are doing here is returning a function and when we call the function that gets returned we place the second set of parentheses after it, so the second set of parentheses invokes the function that got returned. 我们在这里所做的只是返回一个函数,当我们调用要返回的函数时,我们将第二组括号放在其后,因此第二组括号将调用返回的函数。

In regards to your mapStateToProps , there is something missing there, I believe it should look like: 关于您的mapStateToProps ,这里缺少一些东西,我相信它应该看起来像:

const mapStateToProps = state => {
  return { images: state.images };
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM