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 引用 listsetList,当我尝试应用函数式编程原则时,它们不是此函数的输入。

关于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>
      ))}
    </>
  )
}

但在某种程度上,两者似乎都是额外的样板文件——当我们查看原始问题时,可能不需要。