我有一个反应组件,它使用 useReducer 钩子来处理动态文本字段的添加和删除。 预期工作如下,
但是当我尝试通过添加/删除进行分派时,state.map 函数将变为未定义状态。 任何形式的帮助都非常感谢
代码片段。
export default function Create(props) {
const initialState = [{}];
function reducer(state, action) {
switch (action.type) {
case 'add':
state = {...state}
console.log(state)
return state;
case 'remove':
return state;
default:
throw new Error();
}
}
const {dialogOpen, data} = {...props}
const [open, setOpen] = React.useState(false);
const [state, dispatch] = React.useReducer(reducer, initialState)
return (
<div>
{
state.map(() => {
return (
<div style={{display: "flex", alignItems: "center"}}>
<ApplicationBudget/>
<IconButton style={{marginLeft: 10}} onClick={() => dispatch({type: 'remove'})}
aria-label="delete" margin={{margin: 2}}>
<DeleteOutlined/>
</IconButton>
<IconButton aria-label="add" onClick={() => dispatch({type: 'add'})}
margin={{margin: 1}}>
<Add/>
</IconButton>
</div>
)
})
}
</div>
);
}
答案 0 :(得分:1)
你的初始状态是一个数组:
initialState = [{}]
所以 state.map
有效(因为 Array
的原型有一个 map
方法)。
但是,在您的减速器中,您返回一个对象:
state = {...state}
您的对象没有 map
方法。
这里有两个问题:
state
变量,而应保持不变并返回一个新状态。像这样工作的一种常见方法是将当前状态传播到返回状态。示例:
function reducer(state, action) {
switch (action.type) {
case 'add':
return [...state, "new entry"];
}
}
更新:我为您添加了一个最小示例。它有一个具有两个动作的减速器。一个和你有同样的错误行为,一个正常工作。
function App() {
const reducer = (state, action) => {
switch (action.type) {
case "add-broken": {
return {...state};
}
case "add-working": {
return [...state, ...action.payload];
}
default: {
throw new Error();
}
}
};
const [state, dispatch] = React.useReducer(reducer, ['a', 'b']);
return (
<div>
<ul>
{state.map(s => <li>{s}</li>)}
</ul>
<button onClick={() => dispatch({type: 'add-broken', payload: 'C'})}>Add (broken)</button>
<button onClick={() => dispatch({type: 'add-working', payload: 'C'})}>Add (working)</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="app"></div>
答案 1 :(得分:0)
感谢 fjc 的帮助。 最终得到以下解决方案
export default function Create(props) {
const initialState = [{}];
function reducer(state, action) {
switch (action.type) {
case 'add':
return [...state, ...action.payload]
case 'remove':
let newState = [...state]
newState.splice(action.payload, 1)
return newState
default:
throw new Error();
}
}
const {dialogOpen, data} = {...props}
const [open, setOpen] = React.useState(false);
const [state, dispatch] = React.useReducer(reducer, initialState)
return (
<div>
{
state.map((budget, index) => {
return (
<div key={index} style={{display: "flex", alignItems: "center"}}>
<ApplicationBudget/>
<IconButton style={{marginLeft: 10}}
onClick={() => dispatch({type: 'remove', payload: index })}
aria-label="delete" margin={{margin: 2}}>
<DeleteOutlined/>
</IconButton>
<IconButton aria-label="add"
onClick={() => dispatch({type: 'add', payload: [{}]})}
margin={{margin: 1}}>
<Add/>
</IconButton>
</div>
)
})
}
</div>
);
}