使用初始状态来反应useState钩子事件处理程序

时间:2019-03-20 16:04:11

标签: javascript reactjs ecmascript-6 react-hooks

我仍然不停地做出反应,但仍在努力查看我在做什么错。我有一个用于调整面板大小的组件,在边缘的onmousedown上更新状态值,然后为mousemove使用事件处理程序,该事件处理程序使用该值,但是在值更改后似乎没有更新。

这是我的代码:

export default memo(() => {
  const [activePoint, setActivePoint] = useState(null); // initial is null

  const handleResize = () => {
    console.log(activePoint); // is null but should be 'top|bottom|left|right'
  };

  const resizerMouseDown = (e, point) => {
    setActivePoint(point); // setting state as 'top|bottom|left|right'
    window.addEventListener('mousemove', handleResize);
    window.addEventListener('mouseup', cleanup); // removed for clarity
  };

  return (
    <div className="interfaceResizeHandler">
      {resizePoints.map(point => (
        <div
          key={ point }
          className={ `interfaceResizeHandler__resizer interfaceResizeHandler__resizer--${ point }` }
          onMouseDown={ e => resizerMouseDown(e, point) }
        />
      ))}
    </div>
  );
});

问题出在handleResize函数上,应该使用最新版本的activePoint,它是字符串top|left|bottom|right,而实际上是null

7 个答案:

答案 0 :(得分:8)

您可以通过设置器功能访问当前状态,因此可以做到:

const handleResize = () => {
  setActivePoint(activePoint => {
    console.log(activePoint);
    return activePoint;
  })
};

答案 1 :(得分:4)

  const [activePoint, setActivePoint] = useState(null); // initial is null

  const handleResize = () => {
    setActivePoint(currentActivePoint => { // call set method to get the value
       console.log(currentActivePoint);  
       return currentActivePoint;       // set the same value, so nothing will change
                                        // or a different value, depends on your use case
    });
  };

答案 2 :(得分:3)

[{"id":[1234],"attributes":[{"typeId":[11],"type":["Main"],"date":["2018-01-01"],"attributes":[{"team":["team1"],"values":{"value1":[1],"value2":[999]}},{"team":["team2"],"values":{"value1":[2],"value2":[888]}}]},{"typeId":[12],"type":["Extra"],"date":["2018-01-02"],"attributes":[{"team":["team1"],"values":{"value1":[3],"value2":[1234]}},{"team":["team2"],"values":{"value1":[4],"value2":[9876]}}]}]}]读取未来值

当前,您的问题是您正在读取过去的价值。因此,当您定义useRef时,它属于该渲染,因此,当您重新渲染事件侦听器时,什么也没有发生,因此它仍从其渲染中读取旧值。

要解决此问题,您应该通过handleResize使用一个保持更新的引用,以便可以读取当前值。

Example (link to jsfiddle):

useRef

答案 3 :(得分:1)

除了敬畏ChrisBrownie55的建议之外,

可以实现自定义钩子,以避免重复此代码,并使用与标准useState几乎相同的方法来使用此解决方案:

// useReferredState.js
import React from "react";

export default function useReferredState(initialValue) {
    const [state, setState] = React.useState(initialValue);
    const reference = React.useRef(state);

    const setReferredState = value => {
        reference.current = value;
        setState(value);
    };

    return [reference, setReferredState];
}


// SomeComponent.js
import React from "react";

const SomeComponent = () => {
    const [someValueRef, setSomeValue] = useReferredState();
    // console.log(someValueRef.current);
};

答案 4 :(得分:1)

对于使用打字稿的人,可以使用以下功能:

export const useReferredState = <T>(
    initialValue: T = undefined
): [T, React.MutableRefObject<T>, React.Dispatch<T>] => {
    const [state, setState] = useState<T>(initialValue);
    const reference = useRef<T>(state);

    const setReferredState = (value) => {
        reference.current = value;
        setState(value);
    };

    return [state, reference, setReferredState];
};

并这样称呼它:

  const [
            recordingState,
            recordingStateRef,
            setRecordingState,
        ] = useReferredState<{ test: true }>();

,当您调用setRecordingState时,它将自动更新参考和状态。

答案 5 :(得分:0)

您可以使用useEffect挂钩,并在每次activePoint更改时初始化事件侦听器。这样,您可以最大程度地减少代码中不必要的引用的使用。

答案 6 :(得分:0)

long x = 5; var example = x < 0 ? "Not Available" : x; 进行回调

除了ChrisBrownie55建议的正确方法之外,您可以使用类似的方法,将eventListener的回调本身使用useRef而不是useRef的值,可能更易于维护。

通过这种方式,您不必担心将来要使用的每个useState都保存在引用中。

只需将useState保存在引用中并在每个渲染器上更新其值:

handleResize

,并使用const hanleResizeRef = useRef(handleResize) hanleResizeRef.current = handleResize; 作为回调,并包装在箭头函数中:

hanleResizeRef

使用自定义钩子的完整代码:

window.addEventListener('mousemove', e => handleResizeRef.current(e));