如何实现根据Redux上按下的按钮从API接收不同数据的按钮?

时间:2019-11-05 09:09:10

标签: javascript reactjs redux

我目前有一个代码可以在页面上显示Api数据。这是一个列表:

API具有以下结构:

{"hij":{
"low":[{......},{.....}],
"all":[{....},{......}]}
}


如何在Redux上实现此类按钮?你能帮我一下吗?我在YouTube上观看了几十个视频,阅读了许多文章,还没有有关如何制作按钮以从API获取不同数据的示例。... 在React上,我是这样实现的(App.js):

class App extends React.Component {
  state = {
    day: 1,
    data: [],
    filteredData: [],
    search: "",
    shift: "low"
  };

  componentDidMount() {
    this.fetchData();
  }

  fetchData = async () => {
    const response = await fetch(`...`);
    const data = (await response.json()).body;
    this.setState({data, shift: Object.keys(data)[0]}, this.filter);
  };

  loadDay = day => {
    this.setState({ day }, this.fetchData);
  };

  updateSearch = e => {
    this.setState({search: e.target.value});
  };

  filter = () => {
    this.setState(({ search, data, shift }) => {
      const s = search.toLowerCase();
      return {
        filteredData: data[shift].filter(n =>
          n.term.toLowerCase().includes(s)
        )
      };
    });
  };

  onClick = ({target: { dataset: { shift }}}) => {
    this.setState(() => ({ shift }), this.filter);
  };

  render() {
    const { search, shift, data, filteredData } = this.state;

    return (
      <div>
        <TableSearch value={search}
           onChange={this.updateSearch} 
            onSearch={this.filter}/>

      {days.map((day, i) => (
             <button key={day} 
             onClick={() => this.loadDay(i)}
             className={i === this.state.day ? "active" : ""}>{day}</button>
         ))}
        <br />
        {Object.keys(data).map(n => (
             <button data-shift={n}
               onClick={this.onClick}
               className={n === shift ? "active" : ""}>{n} shift</button>
        ))}
        <TableData data={filteredData} />
      </div>
    );
  }
}



1 个答案:

答案 0 :(得分:2)

要使它成为与Redux兼容的应用程序,有许多事情需要更改,您最不用担心的就是按钮。因此,这不是直接回答按钮部分,而是您的应用程序对Redux的带注释的重构:

import { createStore, applyMiddleware } from 'redux';
import { connect, Provider } from 'react-redux';
import thunk from 'redux-thunk';

// Take out common functionality into seperate functions, such as 
// running a search here.
function searchFilter (search, data) {
  return data.filter(n => n.term.toLowerCase().includes(search));
}

// This is your reducer function which updates the global redux state,
// depending on which action you dispatch:
// see https://redux.js.org/basics/reducers
function reducer (state = {}, action) {

  state = { ...state }

  switch (action.type) {
    case 'SET_SEARCH':
      state.search = action.search.toLowerCase();
      break;
    case 'RUN_FILTER':
      state.shift = action.shift || state.shift;
      state.search = action.search || state.search;
      state.filteredData = searchFilter(state.search, state.data[state.shift]);
      break;
    case 'LOAD_DATA_START':
      state.day = action.day;
      break;
    case 'LOAD_DATA_END':
      state.data = action.data;
      state.shift = Object.keys(data)[0];
      state.filteredData = searchFilter(state.search, state.data[state.shift]);
      break;
  }

  return state;

}

// This is your store object which contains an initial state, and a reducer
// that will be used for dispatched actions.
// see https://redux.js.org/basics/store
//
// Redux-thunk is used as middleware to support async data fetching which you will
// also need to read up on, although you don't really need to know how it
// works at first.
// see https://github.com/reduxjs/redux-thunk
const store = createStore(
  reducer,
  {
    day: 1,
    data: [],
    filteredData: [],
    search: "",
    shift: "departure"
  },
  applyMiddleware(thunk)
);

// This is a "thunk" called fetch data, again you can read more
// about thunks in the redux-thunk docs
// see https://github.com/reduxjs/redux-thunk
function fetchData (day) {
  return async (dispatch) => {
    dispatch({ type: 'LOAD_DATA_START', day });

    const response = await fetch(`https://api.iev.aero/api/flights/${days[this.state.day]}`);
    const data = (await response.json()).body;

    dispatch({ type: 'LOAD_DATA_END', data });
  }
}

const days = ["23-08-2019", "24-08-2019", "25-08-2019"];

// Stripped down component, it does not handle any of its own state
// all state is passed to it through the redux connect HOC.
class Root extends React.Component {

  componentDidMount() {
    this.props.onFetchData(this.props.day);
  }

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

    return (
      <div>
        <TableSearch value={search}
          onChange={(e) => onSetSearch(e.target.value)} 
          onSearch={() => onFilter()} />

        {days.map((day, i) => (
          <button key={day} 
            onClick={() => onFetchData(day)}
            className={i === day ? "active" : ""}>{day}</button>
        ))}

        <br />

        {Object.keys(data).map(n => (
          <button data-shift={n}
            onClick={(e) => onFilter({ shift: e.target.dataset.shift })}
            className={n === shift ? "active" : ""}>{n} shift</button>
        ))}

        <TableData data={filteredData} />

      </div>
    );
  }

}

// This is the "connected" version of the component, which is 
// your component wrapped in a connect HOC. When the reducer function
// is run, the two functions below will be executed and your component
// inside will re render.
//
// You can read more about this one in react-redux
// https://react-redux.js.org/
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))
  })
);

// This is your top level component that you would call in ReactDOM.render
// The Provider component is part of react-redux, and you can read about it
// there, but in most cases it is sufficient to drop it at the very top level
// of your application.
// https://react-redux.js.org/
const App = () => (
  <Provider store={store}>
    <ConnectedRoot />
  </Provider>
);