我试图将redux合并到我制作的现有应用程序中,该应用程序从api获取geoJSON并加载地图并可以进行过滤。
在使用redux之前,我创建了一个句柄函数来过滤状态并进行更新。然后将其向下传递给子组件以在按钮中使用。
在学习Redux的过程中,我正在努力弄清楚我将该代码放在哪里。它是放在我的减速器中还是作为动作创建者?有人可以建议吗?
父类组件内部的句柄函数
filterMaterial = (name) => {
const filteredData = this.state.geoJSON.features.filter(item => item.properties.material === name ? item : false);
const newobj = {
type: "FeatureCollection",
totalFeatures: filteredData.length,
features: filteredData
}
this.setState({mapJSON: newobj});
}
下面是我当前与redux相关的代码,如何将以上代码转换为与redux一起使用?
操作
import { DATA_LOADED } from "../constants/action-types";
export function getData() {
return function(dispatch) {
const request = async () => {
const url = `http://localhost:4000/api/assets`;
try {
const response = await fetch(url);
const data = await response.json();
dispatch({ type: DATA_LOADED, payload: data });
} catch(err) {
console.log(err)
}
}
return request();
};
}
减速器
import { DATA_LOADED } from "../constants/action-types";
const initialState = {
geoJSON: {},
mapJSON: {}
};
function rootReducer(state = initialState, action) {
if(action.type === DATA_LOADED) {
return Object.assign({}, state, {
geoJSON: state.geoJSON = {...action.payload},
mapJSON: state.mapJSON = {...action.payload}
});
}
return state;
};
export default rootReducer;
商店
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "../reducers/index";
import thunk from "redux-thunk";
const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
rootReducer,
storeEnhancers(applyMiddleware(thunk))
);
export default store;
父级组件
//...code
const mapStateToProps = state => {
return {
geoJSON: state.geoJSON,
mapJSON: state.mapJSON
};
};
export default connect(mapStateToProps, {getData})(FetchData);
答案 0 :(得分:2)
除了减速器中的一些状态对象突变之外,您所拥有的一切看起来还不错。
import { DATA_LOADED } from "../constants/action-types";
const initialState = {
geoJSON: {},
mapJSON: {}
};
function rootReducer(state = initialState, action) {
if(action.type === DATA_LOADED) {
return Object.assign({}, state, {
geoJSON: state.geoJSON = {...action.payload}, // this will mutate current state!
mapJSON: state.mapJSON = {...action.payload}
});
}
return state;
};
export default rootReducer;
模式是简单地创建新的状态对象,并在当前状态中扩展您想要保留的状态。
import { DATA_LOADED } from "../constants/action-types";
const initialState = {
geoJSON: {},
mapJSON: {}
};
function rootReducer(state = initialState, action) {
if(action.type === DATA_LOADED) {
const { geoJSON, mapJSON } = action.payload;
return {
...state,
geoJSON,
mapJSON
});
}
return state;
};
export default rootReducer;
此OFC假定每次更新状态时,您只需替换现有的geoJSON
和mapJSON
值。如果您需要合并新的有效负载,则将使用以下模式。
return {
...state,
geoJSON: { ...state.geoJSON, ...geoJSON },
mapJSON: { ...state.mapJSON, ...mapJSON },
});
Reducer要记住的重要一点是它们应该是具有零副作用的纯函数,并且它们应该 始终 返回新的对象引用,以便进行浅引用相等性检查对DOM差异做出反应。
至于过滤。筛选时,通常会保留真相来源,而只是筛选数据的“视图”。在这种情况下,您肯定要在组件内进行过滤。它将从connect
HOC中获取地理数据作为道具,因此您可以直接从道具对象中进行过滤。
const filteredData = this.props.geoJSON.features.filter(item => item.properties.material === name ? item : false);
const newobj = {
type: "FeatureCollection",
totalFeatures: filteredData.length,
features: filteredData
}
这可以在render
函数中完成。不需要在本地组件状态下复制数据。
如果我按照您的建议通过道具过滤状态,我该怎么办 更新状态?你只是打电话给调度员吗?
是的,更新redux状态是通过调度的动作和reducer完成的。也许我们对“过滤器”的含义并不完全相同。通常,当您过滤数据时,您只是将源过滤到精简的数据集中进行显示。这不需要更改或更新源。将其与诸如在源数据中添加或删除条目(注意:在实现中,过滤器功能可能用于删除特定数据)之类的事情进行对比,在该操作中必须分派操作以对状态进行操作,即用户单击删除按钮。
如果您需要持久保存过滤后的数据,那么我建议您保留原始数据不变,而将过滤条件存储在状态中。然后,您可以使用两个状态切片来得出显示的内容。
如果我将其包装在函数中,也可以将其传递给子级 组件?
当然,您几乎可以将任何东西作为对子组件的支持。 Redux,React上下文API和高阶组件的优点在于,它们都有助于解决道具钻探的问题。 任何组件都可以订阅redux存储并提取 just 所需的数据,并访问dispatch
函数,甚至可以将动作创建者自动包裹在呼叫dispatch
。因此,尽管您可以创建使用分派并将其传递给子代的函数,但这并不是react中redux的推荐用例。
答案 1 :(得分:1)
您是永久更改商店状态还是仅呈现已过滤的组件状态?如果只是渲染,则将其保留在组件中。如果您打算永久更新存储状态,则仍将其保留在组件中,但将返回值(newobj)作为有效内容分发给动作创建者:
动作创建者文件示例:
const ACTION_TYPE_NAME = "ACTION_TYPE_NAME";
export default function actionCreatorName(payloadName) {
return { type: ACTION_TYPE, payloadName }
}
减速器示例:
//set default state here
const initial = {mapJSON: "", geoJSON: ""}
export default function rootReducer(state = initial, action) {
switch (action.type) {
case ACTION_TYPE_NAME:
//return new state object with updated prop
return {...state, mapJSON: action.payloadName}
default:
return state
}
}
示例组件连接/操作分派:
import React from 'react'
import { connect } from 'react-redux'
import actionCreatorName from '../actions/actionCreatorName.js'
//any state you want to import from store
const mapStateToProps = state => {
return {
mapJSON: state.mapJSON,
geoJSON: state.geoJSON
}
}
//any imported action creators you want to bind with dispatch
const mapDispatchToProps = {
actionCreatorName
}
//set the above as props
let ComponentName = ({actionCreatorName, mapJSON, geoJSON}) => {
const yourFunction = () => {
//do stuff then dispatch
actionCreatorName(newobj)
}
return (
<div onClick={yourFunction}></div>
)
}
ComponentName = connect(
mapStateToProps,
mapDispatchToProps
)(ComponentName)
export default ComponentName