React 函数组件中的函数式编程
Functional programming in React Function Components
如果我想遵循 FP 原则,我的内部函数应该在这个 React 函数组件中是纯函数吗?还是由于该组件的性质以及它是功能组件的事实,它是否可以? (因为,我的技术描述可能不正确,它是一种闭包。因此它需要更改和访问状态。)
const Component = ({data, onChange}) => {
const [list, setList] = useState(data);
const removeItem = (id) => {
const newList = list.filter(item => item.id !== id);
setList(newList);
... do something else here onChange ...
}
return (
<>
{list.map(({text, id}) => (
<div onClick={() => removeItem(id) }>{text}</div>
))}
</>
)
}
更新
我特别应该关注 removeItem
引用 list
和 setList
,当我尝试应用函数式编程原则时,它们不是此函数的输入。
关于list
:
const newList = list.filter((item) => item.id !== id);
使用高阶函数.filter()
我们正在制作它的副本并将其作为不可变数据保存。因此,我们在旧列表的基础上创建了一个新列表,过滤条件保持不变。
关于setList
:
setList(newList);
避免变异的解决方法是使用展开运算符将状态对象中的所有 key/value 对展开到新的状态对象中(类似于我们通常在 reducer 中所做的想法),它看起来像这样:
setList({ ...list, list: newList });
此外,我们可以使用函数委托来封装允许组合函数的方法(作为数据传递)
const isDifferentID = item => item.id !== id;
const removeItem = (id, setList, list) => setList({...list, list: list.filter(item => isDifferentID(item))});
最后,大家一起:
const Component = ({data, onChange}) => {
const [list, setList] = useState(data);
const isDifferentID = item => item.id !== id;
const removeItem = (id, setList, list) => setList({...list, list: list.filter(item => isDifferentID(item))});
return (
<>
{list.map(({text, id, setList, list}) => (
<div onClick={() => removeItem(id, setList, list)}>{text}</div>
))}
</>
)
}
最后的笔记:
函数式风格 discourages
具有副作用的函数会修改内部状态或进行其他在函数 return 值中不可见的更改。我们的目标是尽可能statelessness and immutability
。
[更新]
谢谢。我喜欢你关于参数的建议我可以看到这增加了函数的可预测性,所以我更新了它以包括:id, setList, list
根据@SuperJumbo 的评论,我认为详细的解决方案是一个同样有效的答案(使用高阶函数)。
const Component = ({data, onChange}) => {
const [list, setList] = useState(data);
const isDifferentID = item => item.id !== id;
const removeItem = (list, setList) => id => setList(list.filter(item => item.id !== id))
const removeListItem = removeItem(list, setList);
return (
<>
{list.map(({text, id, setList, list}) => (
<div onClick={() => removeListItem(id)}>{text}</div>
))}
</>
)
}
但在某种程度上,两者似乎都是额外的样板文件——当我们查看原始问题时,可能不需要。
如果我想遵循 FP 原则,我的内部函数应该在这个 React 函数组件中是纯函数吗?还是由于该组件的性质以及它是功能组件的事实,它是否可以? (因为,我的技术描述可能不正确,它是一种闭包。因此它需要更改和访问状态。)
const Component = ({data, onChange}) => {
const [list, setList] = useState(data);
const removeItem = (id) => {
const newList = list.filter(item => item.id !== id);
setList(newList);
... do something else here onChange ...
}
return (
<>
{list.map(({text, id}) => (
<div onClick={() => removeItem(id) }>{text}</div>
))}
</>
)
}
更新
我特别应该关注 removeItem
引用 list
和 setList
,当我尝试应用函数式编程原则时,它们不是此函数的输入。
关于list
:
const newList = list.filter((item) => item.id !== id);
使用高阶函数.filter()
我们正在制作它的副本并将其作为不可变数据保存。因此,我们在旧列表的基础上创建了一个新列表,过滤条件保持不变。
关于setList
:
setList(newList);
避免变异的解决方法是使用展开运算符将状态对象中的所有 key/value 对展开到新的状态对象中(类似于我们通常在 reducer 中所做的想法),它看起来像这样:
setList({ ...list, list: newList });
此外,我们可以使用函数委托来封装允许组合函数的方法(作为数据传递)
const isDifferentID = item => item.id !== id;
const removeItem = (id, setList, list) => setList({...list, list: list.filter(item => isDifferentID(item))});
最后,大家一起:
const Component = ({data, onChange}) => {
const [list, setList] = useState(data);
const isDifferentID = item => item.id !== id;
const removeItem = (id, setList, list) => setList({...list, list: list.filter(item => isDifferentID(item))});
return (
<>
{list.map(({text, id, setList, list}) => (
<div onClick={() => removeItem(id, setList, list)}>{text}</div>
))}
</>
)
}
最后的笔记:
函数式风格 discourages
具有副作用的函数会修改内部状态或进行其他在函数 return 值中不可见的更改。我们的目标是尽可能statelessness and immutability
。
[更新]
谢谢。我喜欢你关于参数的建议我可以看到这增加了函数的可预测性,所以我更新了它以包括:id, setList, list
根据@SuperJumbo 的评论,我认为详细的解决方案是一个同样有效的答案(使用高阶函数)。
const Component = ({data, onChange}) => {
const [list, setList] = useState(data);
const isDifferentID = item => item.id !== id;
const removeItem = (list, setList) => id => setList(list.filter(item => item.id !== id))
const removeListItem = removeItem(list, setList);
return (
<>
{list.map(({text, id, setList, list}) => (
<div onClick={() => removeListItem(id)}>{text}</div>
))}
</>
)
}
但在某种程度上,两者似乎都是额外的样板文件——当我们查看原始问题时,可能不需要。