如何更改connect方法中要使用的部分代码(要使用mapStateToProps),并将此处显示的操作移至index.js(操作)?

时间:2019-11-18 11:15:19

标签: javascript reactjs redux

我有一个关于Redux-React的应用程序。您只能在沙箱中查看其工作方式:

https://codesandbox.io/s/redux-ant-design-filter-table-column-with-slider-i55md

我的应用程序正常运行,但是在app.js文件(主要组件)中对此代码的注释不好:

export const ConnectedRoot = connect(
  state => state,
  dispatch => ({
    onFilter: args => dispatch({ type: "RUN_FILTER", ...args }),
    onSetSearch: search => dispatch({ type: "SET_SEARCH", search }),
    onFetchData: day => dispatch(fetchData(day))
  })
)(Root);

他们告诉我,我需要重新编写此代码段。因为只有mapStateToProps方法或mapDispatchToProps方法以及组件本身需要将pass(give)传递给connect方法。  并且动作“ RUN_FILTER”和“ SET_SEARCH”必须在index.js(动作)文件中。

也告诉我,在index.js文件中:


ReactDOM.render(
  <Provider store={store}>
    <ConnectedRoot />
  </Provider>,
  document.getElementById("root")
);

应该是这样的:

ReactDOM.render(
  <Provider store={store}>
    <Root/>
  </Provider>,
  document.getElementById("root")
);

如何更改app.js(容器),index.js(操作)和index.js文件以满足这些要求并使应用程序正常工作?我刚开始研究Redux,当我开始将某物移动到某个地方时,我会遇到无尽的错误。因此,需要经验丰富的程序员的帮助。
我的代码:
app.js(容器):

import React from "react";
import { Component } from "react";
import { connect } from "react-redux";
import { fetchData } from "../actions";
import TableData from "../components/TableData";
import TableSearch from "../components/TableSearch";
import Header from "../components/Header";
import Footer from "../components/Footer";
import "../components/app.css";

export function searchFilter(search, data) {
  return data.filter(n => n["planeTypeID.code"].toLowerCase().includes(search));
}

const days = ["12-11-2019", "13-11-2019", "14-11-2019"];

class Root extends React.Component {
  componentDidMount() {
    this.props.onFetchData(days[this.props.propReducer.day]);
  }

  render() {
    const { onFilter, onSetSearch, onFetchData } = this.props;
    const { search, shift, data, filteredData } = this.props.propReducer;

    return (
      <div>
        <div className="content">

        <Header/>
        <br/>
        <div className="searchTitle">SEARCH FLIGHT</div>
             <br/>
        <TableSearch value={search} onChange={e => onSetSearch(e.target.value)} 
         onSearch={value => onFilter({ search: value })}/>
             <br/>
             <br/>
        <div className="buttonShift">
          {data && Object.keys(data).map(n => (
            <button data-shift={n} onClick={e => onFilter({ shift: e.target.dataset.shift })} className={n === shift ? "active" : "noActive"}>
                {n}
            </button>
          ))}
        </div>

        <div className="row">
        <span className="title">Yesterday: </span><span className="title">Today: </span><span className="title">Tomorrow: </span>
        </div>

        <div className="buttonDays">
          {days && days.map((day, i) => (
            <button  key={day} onClick={() => onFetchData(day)} className="buttonDaysOne">
                {day} 
            </button>
          ))}
        </div>

        {data && <TableData data={filteredData} />}
          </div>
        <Footer/>
      </div>
    );
  }
}

export const ConnectedRoot = connect(
  state => state,
  dispatch => ({
    onFilter: args => dispatch({ type: "RUN_FILTER", ...args }),
    onSetSearch: search => dispatch({ type: "SET_SEARCH", search }),
    onFetchData: day => dispatch(fetchData(day))
  })
)(Root);

index.js(操作):

import { days } from "../containers/app";

export function fetchData(day) {
  return async dispatch => {
    dispatch({ type: "LOAD_DATA_START", day });
    const response = await fetch(`https://website.page.internal/someapi/first/${day}`);
    const data = (await response.json()).body;
    dispatch({ type: "LOAD_DATA_END", payload: { data, day } });
  };
}
export function setShift(shift) {
  return async dispatch => {
    dispatch({ type: "SET_SHIFT", shift });
  };
}

index.js:

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reducer from "./reducers";
import thunk from "redux-thunk";
import { ConnectedRoot } from './containers/app';

const store = createStore(
  reducer, 
  {
    propReducer: {
      day: 1,
      data: [],
      filteredData: [],
      search: "",
      shift: "departure"
    }
  },
  applyMiddleware(thunk)
);

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRoot />
  </Provider>,
  document.getElementById("root")
);

1 个答案:

答案 0 :(得分:1)

已为可能感兴趣的其他人更新了最终代码。

我现在可能没有足够的时间完全回答您的问题,但是我认为即使是部分回答也可能有所帮助。我将随时更新此内容。

您的动作文件被更准确地称为动作创建者文件,并且不应实际分发任何内容。通常,您连接的组件将调度操作。因此,请记住,这是更新的actions.js:

// actions/index.js
export const loadDataStart = day => ({
  type: "LOAD_DATA_START",
  payload: day
});
export const loadDataEnd = (data, day) => ({
  type: "LOAD_DATA_END",
  payload: { data, day }
});
export const setShift = shift => ({
  type: "SET_SHIFT",
  payload: shift
});
export const runFilter = args => ({
  type: "RUN_FILTER",
  payload: { ...args }
});
export const setSearch = search => ({
  type: "SET_SEARCH",
  payload: search
});
// containers/App.js
// This component should be changed to a functional component, especially in
// light of the componentWillUpdate warnings in the console.

// Since this project is already very different, I decided not to do that now
// to give you a better chance at understanding what I did.

import React, { Component } from "react";
import { connect } from "react-redux";

// NOTE: I consolidated the imports. see the ./components for more info
import { TableData, TableSearch } from "../components";

import * as actions from "../actions";

const days = ["12-11-2019", "13-11-2019", "14-11-2019"];

export function setShift(shift) {
  return async dispatch => {
    dispatch({ type: "SET_SHIFT", shift });
  };
}

class App extends Component {
  constructor(props) {
    super(props);
    this.fetchData = this.fetchData.bind(this);
  }

  // I extracted fetchData into a class function and bound it
  // in a new constructor, made only for this purpose.
  fetchData(day) {
    // I couldn't make an async class function, so I cheated
    // and embedded one here instead.
    const doFetchData = async () => {
      const { loadDataStart, loadDataEnd } = this.props;
      await loadDataStart(day);
      const response = await fetch(
        `https://api.iev.aero/api/flights/${days[day]}`
      );
      const data = (await response.json()).body;
      await loadDataEnd(data, day);
    };

    // call the embedded "cheat" function
    doFetchData();
  }
  componentDidMount() {
    this.fetchData(this.props.propReducer.day);
  }

  render() {
    const { onFilter, onSetSearch } = this.props;
    const { search, shift, data, filteredData } = this.props.propReducer;

    return (
      <div>
        <div className="content">
          <br />
          <div className="searchTitle">SEARCH FLIGHT</div>
          <br />
          <TableSearch
            value={search}
            onChange={e => onSetSearch(e.target.value)}
            onSearch={value => onFilter({ search: value })}
          />
          <br />
          <br />
          <div className="buttonShift">
            {data &&
              Object.keys(data).map((n, idx) => (
                <button
                  key={idx}
                  data-shift={n}
                  onClick={e => onFilter({ shift: e.target.dataset.shift })}
                  className={n === shift ? "active" : "noActive"}
                >
                  {n}
                </button>
              ))}
          </div>

          <div className="row">
            <span className="title">Yesterday: </span>
            <span className="title">Today: </span>
            <span className="title">Tomorrow: </span>
          </div>

          <div className="buttonDays">
            {days &&
              days.map((day, daysIndex) => (
                <button
                  key={day}
                  onClick={() => {
                    this.fetchData(daysIndex);
                  }}
                  className="buttonDaysOne"
                >
                  {day}
                </button>
              ))}
          </div>

          {data && <TableData data={filteredData} />}
        </div>
      </div>
    );
  }
}

export default connect(
  state => state,
  {
    // Note that fetchData is no longer an action
    onFilter: actions.runFilter,
    onSetSearch: actions.setSearch,
    loadDataStart: actions.loadDataStart,
    loadDataEnd: actions.loadDataEnd
  }
)(App);
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reducer from "./reducers";
import thunk from "redux-thunk";
import App from "./containers/App";

const initialState = {
  propReducer: {
    day: 1,
    data: [],
    filteredData: [],
    search: "",
    shift: "departure"
  }
};

const store = createStore(reducer, initialState, applyMiddleware(thunk));

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);
// utils.js
export function searchFilter(search, data) {
  return data.filter(n => n["planeTypeID.code"].toLowerCase().includes(search));
}
// reducers/airplanes.js
import { searchFilter } from "../utils";

const actions = {
  SET_SHIFT: (state, action) => ({
    ...state,
    shift: action.payload.shift
  }),
  SET_SEARCH: (state, action) => {
    return {
      ...state,
      search: action.payload.toLowerCase()
    };
  },
  RUN_FILTER: (state, action) => {
    const { shift = state.shift, search = state.search } = action.payload;
    const newData = state.data[shift].filter(x =>
      x["planeTypeID.code"].toLowerCase().includes(search)
    );

    const filteredData = searchFilter(state.search, newData);
    return {
      ...state,
      search,
      filteredData,
      shift
    };
  },
  LOAD_DATA_START: (state, action) => ({ ...state, day: action.payload.day }),
  LOAD_DATA_END: (state, action) => {
    const { data, search: actionSearch } = action.payload;
    const { shift, search: stateSearch } = state;
    const newData = data[shift].filter(x => {
      try {
        const needle = x["planeTypeID.code"].toLowerCase();
        return needle.includes(actionSearch || stateSearch);
      } catch (err) {
        // TypeError: Cannot read property 'toLowerCase' of undefined
        // this means that the code doesn't exist
        return false;
      }
    });
    const filteredData = searchFilter(state.search, newData);
    return {
      ...state,
      data,
      shift: Object.keys(data)[0],
      filteredData
    };
  }
};

export function reducer(state = {}, action) {
  if (action.type in actions) {
    return actions[action.type](state, action);
  }
  return state;
}
相关问题