Ad

How To Change Some Part Of The Code In The Connect Method(to Be Used MapStateToProps)and That Action Which Are Present Here Move To Index.js(action)?

- 1 answer

I have an application written on Redux-React. You can just see how it works in sandbox:

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

My app works, but I got bad comments on this code in the app.js file(main component):

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

They told me that I needed to rework this code snippet. Because only the mapStateToProps method or the mapDispatchToProps method and the component itself need pass(give) to the connect method. and actions "RUN_FILTER" and "SET_SEARCH" must be in the index.js (actions) file.

Also told me, that in the index.js file instead:


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

It should be something like this:

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

How do I change the app.js(containers) and index.js(actions) and index.js files to meet these requirements and make the application work? I just started studying Redux and when I start to move something somewhere, I have an endless chain of errors. Therefore, the help of more experienced programmers is needed.
My code:
app.js(containers):

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(actions):

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

Ad

Answer

Updated with the final code for others who may be interested.

I may not have enough time to fully answer your question now, but I thought that even a partial answer might help. I'll update this one as I go.

Your actions file is more accurately known as an actions creator file and is not supposed to actually dispatch anything itself. Generally, your connected components will dispatch actions. So with that in mind, here's the updated 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;
}
Ad
source: stackoverflow.com
Ad