如何从React钩子链接useState()方法

时间:2020-07-20 14:48:05

标签: reactjs functional-programming react-hooks method-chaining

是否可以链接React钩子?如果可以,怎么办?

钩子的典型应用如下:

const [inv, updateInventory] = useState([])
a = ["cheese", "bread", "apples"]
b = a.filter(isDairy)
updateInventory(b)

我们也可以这样做,但是它不是链式的:

const [inv, updateInventory] = useState([])
a =  ["cheese", "bread", "apples"]
updateInventory(a.filter(isDairy))

我想要的是功能样式的链钩:

const [inv, updateInventory] = useState([])
a =  ["cheese", "bread", "apples"]
a.filter(isDairy).updateInventory()

可以修改钩子来获取this的状态吗?

2 个答案:

答案 0 :(得分:1)

正确的用法是:

updateInventory([...a, "cheddar"].quicksort().filter("cheese"))

但是,如果您确实想要这种链接,请查看how to edit the array prototype

实际上不建议这样做,因为该方法将在所有阵列上可用。

答案 1 :(得分:1)

我认为潜在的问题是您不清楚方法链(可能还有钩子)实际发生了什么。具体问题:

可以修改钩子来获取this的状态吗?

完全没有道理。因此,让我们分解一下为什么,然后最后返回您可以如何实现这一目标。


对于方法链接,让我们尝试使用具有两个重要属性的两个方法.filter.map的简单示例。

  1. 它们实际上返回数组(与.push不同,后者返回数组的新长度);和
  2. 它们实际上存在于数组中(与.quicksort不同,function isDairy(item) { return ["cheese", "milk"].includes(item); } function getPrice(item) { return { bread: 0.58, cheese: 0.80, apples: 0.47, milk: 1.01 }[item]; } const inStock = ["bread", "cheese", "apples"]; inStock .filter(isDairy) .map((item) => ({ item, price: getPrice(item) })); // => [{ item: "cheese", price: 0.8 }] 既不存在于数组中也不存在您调用它的整数)。
const filteredStock = stock.filter(isDairy);
// => ["cheese"]
const pricedFilteredStock = filteredStock.map((item) => ({ item, price: getPrice(item) }));
// => [{ item: "cheese", price: 0.8 }]

这里没有什么特别的事情发生,您正在调用的每个方法都会返回一个新数组,您还可以在该方法上调用数组具有的任何方法。您可以分配中间步骤并获得相同的结果:

map(callable, iterable)

以下情况不是

  • 这些是独立的函数(例如在item.name中的Python中);或
  • 除了访问name上名为item的属性之外,filter语法还没有做任何事情。

如果我尝试将filter(isDairy, inStock); 方法用作独立功能:

function allUppercase() { 
  return this.map((item) => item.toUpperCase());
}

inStock.allUppercase();

那将是ReferenceError,或者如果我定义了另一个函数并试图像访问数组上的prop一样尝试访问它:

isStock.allUppercase

这将是TypeError(因为undefinedundefined,而allUppercase.bind(inStock)()不可调用)。

请注意,您可以allUppercase.call(inStock)(或更整洁的this); JavaScript确实可以为函数设置useState


使用const [thing, setThing] = useState(initialValue); 钩子时,您正在调用一个函数,该函数返回包含两个对象的数组,并将该数组分解为两个局部变量:

const result = useState(initialValue);
const thing = result[0];
const setThing = result[1];

等效于:

thing

setThingconst [foo, bar] = useState("baz")的命名只是一个约定;实际上,我们正在按位置访问这两个对象(当前值和设置函数)。他们没有自己的名字,您可以进行setThing.bind(但...没有)。

由于setter是一个函数,您可能想知道是否可以在此处使用setThing,但是如果this 编写为使用this(我没有(不考虑实现,因为它并不直接相关),如果您更改const [basket, setBasket] = useState([]); // ^^^^^^^^^ inStock.filter(...).map(...).setBasket(); // ^^^^^^^^^ 是什么,不是会很高兴!


因此,当您尝试执行此操作时会合在一起:

setBasket

与上面的示例一样,这是一个TypeError,因为.map返回的数组上不存在setBasket。就JavaScript而言,相同的“单词” .map(...)出现两次的事实是完全不相关的。一个是局部变量,另一个是数组上的prop,它们之间没有任何联系。

setBasket返回一个 new 数组,我们还没有对其进行引用,因此进行此工作的唯一方法是确保 all 数组具有Object.defineProperty(Array.prototype, "setBasket", { value () { setBasket(this); }, }); 方法,这意味着修补原型(如adding custom functions into Array.prototype所述):

setBasket

这里的一个问题是 function useEffect是通过闭包访问的,因此它需要发生在定义了钩子的组件内部,因此每次都要对其进行定义呈现了组件(或者您将转到Object.defineProperty(Array.prototype, "andCall", { value (func) { return func(this); }, }); ),这是一个问题,因为您不能重新按编写的方式定义该方法...

但是让我们忽略一下,因为更大的问题是,即使应用程序中不相关的应用程序中的每个数组现在都具有该方法。如果您有多个状态挂钩(在任何非平凡的应用程序中都可能出现),则您的数组将在全局范围内获得许多仅在较小的局部范围内使用的方法。


一种更可行的方法是添加一个通用方法,该方法可用于将 any 钩子(实际上是任何 function )应用于数组:

inStock.filter(...).map(...).andCall(setBasket);

这可以全局添加一次,并用于应用任何相关的钩子:

declare global {
    interface Array<T> {
        andCall<S>(func: (arr: Array<T>) => S): S;
    }
}

请注意,如果您使用TypeScript,还必须将定义添加到全局数组类型,例如:

pg_catalog