如何在React Hooks上使用“ SetState”回调



React Hooks引入了用于设置组件状态的useState。但是,我该如何使用钩子替换如下代码:

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

我想在州更新后做某事。

我知道我可以使用useEffect来做额外的事情,但是我必须检查状态上一个需要位代码的值。我正在寻找一个可以与useState钩一起使用的简单解决方案。

您需要使用useEffect挂钩来实现此目的。

const [counter, setCounter] = useState(0);
const doSomething = () => {
  setCounter(123);
}
useEffect(() => {
   console.log('Do something after counter has changed', counter);
}, [counter]);

如果您希望在第一个初始渲染中忽略useEffect回调,请相应地修改代码:

import React, { useEffect, useRef } from 'react';
const [counter, setCounter] = useState(0);
const didMount = useRef(false);
const doSomething = () => {
  setCounter(123);
}
useEffect(() => {
  // Return early, if this is the first render:
  if ( !didMount.current ) {
    return didMount.current = true;
  }
  // Paste code to be executed on subsequent renders:
  console.log('Do something after counter has changed', counter);
}, [counter]);

如果要更新以前的状态,则可以在钩子中这样做:

const [count, setCount] = useState(0);

setCount(previousCount => previousCount + 1);

文档:

  • 在下一个渲染之前多次更新同一状态
  • 遗产:功能性更高

模拟setState带有useEffect回调,仅在状态更新上启动(不是初始状态(:

const [state, setState] = useState({ name: "Michael" })
const isFirstRender = useRef(true)
useEffect(() => {
  if (isFirstRender.current) {
    isFirstRender.current = false // toggle flag after first render/mounting
    return;
  }
  console.log(state) // do something after state has updated
}, [state])

自定义钩useEffectUpdate

function useEffectUpdate(callback) {
  const isFirstRender = useRef(true);
  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false; // toggle flag after first render/mounting
      return;
    }
    callback(); // performing action after state has updated
  }, [callback]);
}
// client usage, given some state dep
const cb = useCallback(() => { console.log(state) }, [state]); // memoize callback
useEffectUpdate(cb);

我认为,使用useEffect不是直观的方式。

我为此创建了一个包装器。在此自定义挂钩中,您可以将回调发送到setState参数而不是useState参数。

我刚刚创建了打字稿版本。因此,如果您需要在JavaScript中使用它,只需从代码中删除某些类型的符号。

用法

const [state, setState] = useStateCallback(1);
setState(2, (n) => {
  console.log(n) // 2
});

声明

import { SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
type Callback<T> = (value?: T) => void;
type DispatchWithCallback<T> = (value: T, callback?: Callback<T>) => void;
function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<SetStateAction<T>>] {
  const [state, _setState] = useState(initialState);
  const callbackRef = useRef<Callback<T>>();
  const isFirstCallbackCall = useRef<boolean>(true);
  const setState = useCallback((setStateAction: SetStateAction<T>, callback?: Callback<T>): void => {
    callbackRef.current = callback;
    _setState(setStateAction);
  }, []);
  useEffect(() => {
    if (isFirstCallbackCall.current) {
      isFirstCallbackCall.current = false;
      return;
    }
    callbackRef.current?.(state);
  }, [state]);
  return [state, setState];
}
export default useStateCallback;

缺点

如果传递的箭头函数引用了变量外部函数,则它将捕获当前值,而不是状态更新后的值。在上面的用法示例中, console.log(state(将打印1而不是2。问题,在我的设置中使用使用效果并没有解决这个问题(我正在从数组多个子组件中更新父母的状态,我需要知道哪些组件更新了数据(。

将SetState包裹在承诺中允许完成后触发任意操作:

import React, {useState} from 'react'
function App() {
  const [count, setCount] = useState(0)
  function handleClick(){
    Promise.resolve()
      .then(() => { setCount(count => count+1)})
      .then(() => console.log(count))
  }

  return (
    <button onClick= {handleClick}> Increase counter </button>
  )
}
export default App;

以下问题使我朝着正确的方向:使用挂钩时,React批处理状态更新功能是否可以?

我写了使用Typescript的自定义钩子,如果有人仍然需要它。

import React, { useEffect, useRef, useState } from "react";
export const useStateWithCallback = <T>(initialState: T): [state: T, setState: (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => void] => {
    const [state, setState] = useState<T>(initialState);
    const callbackRef = useRef<(updated: T) => void>();
    const handleSetState = (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => {
        callbackRef.current = callback;
        setState(updatedState);
    };
    useEffect(() => {
        if (typeof callbackRef.current === "function") {
            callbackRef.current(state);
            callbackRef.current = undefined;
        }
    }, [state]);
    return [state, handleSetState];
}

setState()升起对组件状态的更改,并告诉React,该组件及其子女需要通过更新的状态重新渲染。

setState方法是异步的,事实上,它不会返回承诺。因此,在我们要更新或调用函数的情况下,可以在SetState函数中称该函数为第二个参数。例如,在上面的情况下,您将函数称为setState回调。

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

上面的代码适用于类组件,但是在功能组件的情况下,我们不能使用setState方法,并且我们可以利用使用效果钩实现相同的结果。

明显的方法是,YPU可以与使用效率一起使用如下:

const [state, setState] = useState({ name: "Michael" })
useEffect(() => {
  console.log(state) // do something after state has updated
}, [state])

,但这也将在第一个渲染上发射,因此我们可以按以下方式更改代码,我们可以在其中检查第一个渲染事件并避免状态渲染。因此,可以通过以下方式完成实现:

我们可以在此处使用用户钩标识第一个渲染。

USEREF挂钩允许我们在功能组件中创建可变变量。这对于访问DOM节点/REACT元素并存储可突变的变量而不触发重新渲染很有用。

const [state, setState] = useState({ name: "Michael" });
const firstTimeRender = useRef(true);
useEffect(() => {
 if (!firstTimeRender.current) {
    console.log(state);
  }
}, [state])
useEffect(() => { 
  firstTimeRender.current = false 
}, [])

您可以使用我知道的以下方式更新后获得最后的状态:

  1. 使用效率
    https://reactjs.org/docs/hooks-reference.html#useeffect
    const [state, setState] = useState({name: "Michael"});
    
    const handleChangeName = () => {
      setState({name: "Jack"});
    }
    
    useEffect(() => {
      console.log(state.name); //"Jack"
      //do something here
    }, [state]);
  1. 功能更新
    https://reactjs.org/docs/hooks-reference.html#functions-updates
    "如果使用以前的状态计算新状态,则可以将函数传递给SetState。该函数将接收上一个值,并返回更新的值。&quot
    const [state, setState] = useState({name: "Michael"});
    const handleChangeName = () => {
      setState({name: "Jack"})
      setState(prevState => {
        console.log(prevState.name);//"Jack"
        //do something here
        // return updated state
        return prevState;
      });
    }
  1. useref
    https://reactjs.org/docs/hooks-reference.html#useref
    "返回的REF对象将在组件的整个生命周期中持续。
    const [state, setState] = useState({name: "Michael"});
    const stateRef = useRef(state);
    stateRef.current  = state;
    const handleClick = () => {
      setState({name: "Jack"});
      setTimeout(() => {
        //it refers to old state object
        console.log(state.name);// "Michael";
        //out of syntheticEvent and after batch update
        console.log(stateRef.current.name);//"Jack"
        //do something here
      }, 0);
    }

在React Syntheticevent处理程序中,SetState是一个批处理更新过程,因此每个状态的变化都将等待并返回新状态。
&quot" setState((并不总是立即更新组件。它可能会批量或推迟更新,直到以后。&quot,
https://reactjs.org/docs/reaect-component.html#settate

这是一个有用的链接
React会保留状态更新的顺序吗?

我有一个用例 设置状态后。我不想将这些参数设置为我的状态,所以我做了一个自定义钩子,这是我的解决方案

import { useState, useCallback, useRef, useEffect } from 'react';
import _isFunction from 'lodash/isFunction';
import _noop from 'lodash/noop';
export const useStateWithCallback = initialState => {
  const [state, setState] = useState(initialState);
  const callbackRef = useRef(_noop);
  const handleStateChange = useCallback((updatedState, callback) => {
    setState(updatedState);
    if (_isFunction(callback)) callbackRef.current = callback;
  }, []);
  useEffect(() => {
    callbackRef.current();
    callbackRef.current = _noop; // to clear the callback after it is executed
  }, [state]);
  return [state, handleStateChange];
};

您的问题非常有效。让我告诉您,默认情况下,默认情况下一次运行一次,并且每次依赖项数组都会更改。

检查下面的示例::

import React,{ useEffect, useState } from "react";
const App = () => {
  const [age, setAge] = useState(0);
  const [ageFlag, setAgeFlag] = useState(false);
  const updateAge = ()=>{
    setAgeFlag(false);
    setAge(age+1);
    setAgeFlag(true);
  };
  useEffect(() => {
    if(!ageFlag){
      console.log('effect called without change - by default');
    }
    else{
      console.log('effect called with change ');
    }
  }, [ageFlag,age]);
  return (
    <form>
      <h2>hooks demo effect.....</h2>
      {age}
      <button onClick={updateAge}>Text</button>
    </form>
  );
}
export default App;

如果您希望使用钩子执行SetState回调,则使用标志变量,并在其他情况下使用Flag变量,或者在使用效果的情况下block block,以便在满足该条件时只能执行该代码块。无论何时效果效果以依赖关系数组而变化,但是如果代码内部效果仅在特定条件下执行。

我们可以编写一个返回的名为 useScheduleNextRenderCallback的钩子ASTARE"功能。致电setState后,我们可以调用"时间表"。功能,传递我们要在下一个渲染上运行的回调。

import { useCallback, useEffect, useRef } from "react";
type ScheduledCallback = () => void;
export const useScheduleNextRenderCallback = () => {
  const ref = useRef<ScheduledCallback>();
  useEffect(() => {
    if (ref.current !== undefined) {
      ref.current();
      ref.current = undefined;
    }
  });
  const schedule = useCallback((fn: ScheduledCallback) => {
    ref.current = fn;
  }, []);
  return schedule;
};

示例用法:

const App = () => {
  const scheduleNextRenderCallback = useScheduleNextRenderCallback();
  const [state, setState] = useState(0);
  const onClick = useCallback(() => {
    setState(state => state + 1);
    scheduleNextRenderCallback(() => {
      console.log("next render");
    });
  }, []);
  return <button onClick={onClick}>click me to update state</button>;
};

减少测试用例:https://stackblitz.com/edit/react-ts-rjd9jk

简单的解决方案,只需安装

npm i使用状态 - allback

import React from 'react';
import { useStateWithCallbackLazy } from "use-state-with-callback";
const initialFilters = {
  smart_filter: "",
};
const MyCallBackComp = () => {
  const [filters, setFilters] = useStateWithCallbackLazy(initialFilters);
  const filterSearchHandle = (e) => {
    setFilters(
      {
        ...filters,
        smart_filter: e,
      },
      (value) => console.log("smartFilters:>", value)
    );
  };
  return (
    <Input
      type="text"
      onChange={(e) => filterSearchHandle(e.target.value)}
      name="filter"
      placeholder="Search any thing..."
    />
  );
};

记入:React usestate回调

编辑

在此处使用Promise似乎仍然推迟执行rerender之后,触发setState两次可能是获得最新状态的最佳解决方案。因为将列出SetState,我们只需要在重新读取之前使用prevState

原始帖子

我只是弄清楚我们是否可以在这里使用Promise让SetState变得等待。

这是我的实验结果,感觉更好,然后使用回调

主要是在 useEffect

中触发分析函数
function useAsyncState(initialState) {
  const [state, setState] = useState(initialState)
  const resolveCb = useRef()
  const handleSetState = (updatedState) => new Promise((resolve, reject) => {
    // force previous promise resolved
    if (typeof resolveCb.current === 'function') {
      resolveCb.current(updatedState)
    }
    resolveCb.current = resolve
    try {
      setState(updatedState)
    } catch(err) {
      resolveCb.current = undefined
      reject(err)
    }
  })
  useEffect(() => {
    if (typeof resolveCb.current === 'function') {
      resolveCb.current(state)
      resolveCb.current = undefined
    }
  }, [state])
  return [state, handleSetState]
}

使用组件

function App() {
  const [count, setCount] = useAsyncState(0)
  const increment = useMemoizedFn(async () => {
    const newCount = await setCount(count + 1)
    console.log(newCount)
  })
  console.log('rerender')
  return (
    <div>
      <h3 onClick={increment}>Hi, {count}</h3>
    </div>
  )
}

我有一个非常特定的用例,在该案例中我需要在DOM中渲染类,然后设置另一个类。这是我的解决方案,我发现它非常优雅。

const [value1, setValue1] = useState({value: 'whatever', onValue: false})

useEffect(() => {
    setValue1(prev => ({
      value: 'whatever',
      onValue: !prev.onValue, 
    }));
}, ['whatever'])
 
useEffect(() => {
// if you want to ensure the render happens before doThing2() then put it in a timeout of 1ms,
  setTimeout(doThing2, 1); 
// or if you are happy to call it immediately after setting value don't include the timeout
 doThing2()

}, [value1.onValue])

我不认为用useref区分安装是否是一种好方法,不是确定使用效果中的值genetated usestate((的更好方法((是否是初始值?

const [val, setVal] = useState(null)
useEffect(() => {
  if (val === null) return
  console.log('not mounted, val updated', val)
}, [val])

直到我们对setState回调的支持为止,我们可以执行普通的JavaScript方式...调用该功能并将新变量直接传递给它。<<<<<<<<<<<<<<<<<<

  const [counter, setCounter] = useState(0);
  const doSomething = () => {
    const newCounter = 123
    setCounter(newCounter);
    doSomethingWCounter(newCounter);
  };
  function doSomethingWCounter(newCounter) {
    console.log(newCounter); // 123
  }

如果您不需要异步更新状态,则可以使用REF来保存值而不是useState

const name = useRef("John");
name.current = "Michael";
console.log(name.current); // will print "Michael" since updating the ref is not async

我探索了使用状态 - 带有票数的NPM库和其他类似的自定义挂钩,但最后我意识到我可以做这样的事情:

const [user, setUser] = React.useState(
  {firstName: 'joe', lastName: 'schmo'}
)
const handleFirstNameChange=(val)=> {
  const updatedUser = {
     ...user,
     firstName: val
  }
  setUser(updatedUser)
  updateDatabase(updatedUser)
}

传递功能怎么样?

const [name, setName] = useState(initialName); 
...
setName(() => {
    const nextName = "Michael";
    console.log(nextName);
    return nextName;
  });

我认为您需要的是useStateuseCallback

  • usestate react doc;
  • usecallback react doc;

示例代码

import React, { useCallback, useState } from 'react';
const Test = () => {
  const [name, setName] = useState("");
  const testCallback = useCallback(() => console.log(name), [name]);
  return (
    <button onClick={() => {
      setName("Michael")
      testCallback();
    }}>Name</button>
  )
};
export default Test;

怎么样:

const [Name, setName] = useState("");
...
onClick={()=>{
setName("Michael")
setName(prevName=>{...}) //prevName is Michael?
}}

使用效果是主要解决方案。但是,正如达里尔(Darryl(提到的那样,使用使用效率并在状态下传递,因为第二个参数具有一个缺陷,该组件将在初始化过程中运行。如果您只想使用更新状态的值运行回调函数,则可以设置局部常数,并在SetState和回调中使用它。

const [counter, setCounter] = useState(0);
const doSomething = () => {
  const updatedNumber = 123;
  setCounter(updatedNumber);
  // now you can "do something" with updatedNumber and don't have to worry about the async nature of setState!
  console.log(updatedNumber);
}

相关内容

  • 没有找到相关文章

最新更新