我有一个关于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")
);
答案 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;
}