【问题标题】:Typescript types of composition Ramda lens with React useState set function具有 React useState 设置功能的打字稿类型的 Ramda 镜头组合
【发布时间】:2022-01-16 01:18:03
【问题描述】:

我正在学习 FP,并试图弄清楚如何在反应中处理事件。 例如,让我们使用以下场景:

interface Todo {
    task: string
    done: boolean
}

interface TodoProps {
    todo: Todo
    onChange: ChangeEventHandler<HTMLInputElement>
}

function TodoItem({todo, onChange}: TodoProps) {
    return (
        <label>
            {todo.task}
            <input type="checkbox" checked={todo.done} onChange={onChange}/>
        </label>
    )
}

function App() {
    const [todo, setTodo] = useState({
        task: "Some task",
        done: false
    });

    const toggleTodo = // I need to implement this

    return (
        <main>
            <TodoItem onChange={toggleTodo} todo={todo}/>
        </main>
    )
}

没什么花哨的,只是基本的待办事项应用程序。 在缺少的函数中,我需要创建具有更新的 done 属性的对象。为此,我创建了 Ramda 的镜头,专注于 done 属性。

const doneLens = lensProp('done');

那么应该很容易完成目标。我所需要的只是将setTodo 与Rambada 的over 组合在一起。

const toggleDone = () => compose(setTodo, over(doneLens, toggleBoolean))(todo)

但这就是问题所在。我收到了这个 ts 错误:

TS2769: No overload matches this call.   
  The last overload gave the following error.     
    Argument of type '<T>(value: T) => T' is not assignable to parameter of type '(x0: unknown, x1: unknown, x2: unknown) => SetStateAction<{ task: string; done: boolean; }>'.       
      Type 'unknown' is not assignable to type 'SetStateAction<{ task: string; done: boolean; }>'.         
        Type 'unknown' is not assignable to type '(prevState: { task: string; done: boolean; }) => { task: string; done: boolean; }'. 

在纯js中这个函数应该可以工作,但是ts不能推断over函数的返回类型。这是合乎逻辑的。 over 是通用的,所以让我们尝试添加显式类型。

const toggleDone = () => compose(setTodo, over<Todo>(doneLens, toggleBoolean))(todo)

我得到:

TS2554: Expected 3 arguments, but got 2.  
  index.d.ts(669, 51): An argument for 'value' was not provided.

TS2769: No overload matches this call.   
  The last overload gave the following error.     
    Argument of type 'Todo' is not assignable to parameter of type '(x0: unknown, x1: unknown, x2: unknown) => SetStateAction<{ task: string; done: boolean; }>'.       
      Type 'Todo' provides no match for the signature '(x0: unknown, x1: unknown, x2: unknown): SetStateAction<{ task: string; done: boolean; }>'.

Ramda 函数默认是柯里化的,但如果我能正确读取错误,似乎当我添加显式类型时柯里化不起作用。

我可以想出解决方法:

const overDone: (t: Todo) => Todo = over(doneLens, toggleBoolean);
const toggleDone = () => compose(setTodo, overDone)(todo);

这是因为overDone 的返回类型与setTodo 的输入类型匹配。

但是,我的问题是。如何修复oneliner? 或者,如果您知道使用镜头、函数组合、useState 挂钩和打字稿处理类似场景的更好方法,我很想知道。

【问题讨论】:

    标签: reactjs typescript functional-programming event-handling ramda.js


    【解决方案1】:

    Todo 类型添加到lensProp 调用中。您也可以将其移出组件,因为您不需要在组件重新渲染时生成函数:

    const doneLens = lensProp<Todo>("done");
    const toggleDone = over(doneLens, not);
    

    由于setTodo 是一个可以调用另一个函数(参见https://reactjs.org/docs/hooks-reference.html#functional-updates)的函数,它将当前的todo 传递给该函数,因此您不需要R.compose

    const toggleTodo = () => setTodo(over(doneLens, not));
    

    这就是您的组件的外观 (sandbox):

    const doneLens = lensProp<Todo>("done");
    const toggleDone = over(doneLens, not);
    
    function App() {
      const [todo, setTodo] = useState({
        task: "Some task",
        done: false
      });
    
      const toggleTodo = () => setTodo(toggleDone);
        
      return (
        <main>
          <TodoItem onChange={toggleTodo} todo={todo} />
        </main>
      );
    }
    

    更简单的选择是使用R.evolve (sandbox):

    const toggleDone = evolve({ done: not });
    
    function App() {
      const [todo, setTodo] = useState({
        task: "Some task",
        done: false
      });
    
      const toggleTodo = () => setTodo(toggleDone);
    
      console.log(todo);
    
      return (
        <main>
          <TodoItem onChange={toggleTodo} todo={todo} />
        </main>
      );
    }
    

    【讨论】:

    • R.evolve 非常方便,我不知道。但我仍然对compose 有疑问。我知道在这种情况下我不需要它,但我只是举个例子,我可以想象更复杂的场景,在这些场景中使用它可能很有用。问题是当我写lensProp&lt;Todo&gt; 时,IDE 对我大喊:TS2558: Expected 0 type arguments, but got 1(它强调了通用参数)。此外,当我尝试撰写 evolve 时,我收到 TS 错误(添加通用参数没有帮助)。我写这个问题的主要原因是compose 中的类型问题。
    • 我还没有遇到sandbox 中的撰写问题。你用的是什么版本的 Ramda 和@types/ramda
    • "@types/ramda": "^0.27.60", 和我用yarn create vite 创建项目选择用 ts 反应并且根本不修改配置
    • 这是一个使用您的原始代码的工作沙箱。如您所见,TS 工作正常。 codesandbox.io/s/typescript-react-ramda-forked-neu64?file=/src/…
    • 好的,我知道了。我想知道很长一段时间为什么在我的本地机器上我有 ts 错误并且在沙箱中相同的代码运行良好。然后我意识到,当我安装ramda 时,我拼错了并安装了ramBda。现在重新安装后一切正常。
    猜你喜欢
    • 2023-01-15
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    • 2020-12-27
    • 1970-01-01
    • 1970-01-01
    • 2021-03-19
    相关资源
    最近更新 更多