首页 > 解决方案 > 如何从自定义 React 钩子返回变异值?

问题描述

如果我的状态与初始状态相比发生变化,我想将我的组件状态传递给自定义钩子并返回 true。


import { useRef, useEffect } from 'react'

export const useDirtyState = (props:any) => {
//Possibly use local state and set it to true and return that

  const isFirstRender = useRef<boolean>(true)
  const isDirty = useRef<boolean>(false)

  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false
      return
    }
    isDirty.current = true
    console.log(isDirty.current) // is true
  }, [props])
  console.log(isDirty.current) // I return this and it is false :(
} 

//In some other component 
const isDirty = useDirtyStaate(state)//Expect this to be true when state is changed

问题是即使道具发生变化,外部console.log也会显示错误,因为我的效果在那之后运行(我猜?)。如何从此钩子返回正确的值?


编辑:我尝试将本地状态添加到钩子并将其设置为 true 并返回它。虽然这种方法有效,但我想知道是否有更清洁的方法,因为它似乎会导致 1 个额外的渲染。

标签: javascriptreactjsreact-hooksuse-effect

解决方案


只需将原始值存储在 ref 中,并将其与渲染期间提供的值进行比较:

const {useRef, useState} = React;

// You can implement the value comparison using your preferred method
function areValuesEqual (value1, value2) {
  return Object.is(value1, value2);
}

/**
 * TS:
 * function useValueChanged <T>(value: T): boolean
 */
function useValueChanged (value) {
  const originalRef = useRef(value);
  return !areValuesEqual(originalRef.current, value);
}

function Example () {
  const renderCountRef = useRef(0);
  renderCountRef.current += 1;
  const [count, setCount] = useState(0);
  const increment = () => setCount(n => n + 1);
  const didChange = useValueChanged(count);
  
  return (
    <div>
      <div>Render count: {renderCountRef.current}</div>
      <div>Changed: {didChange ? 'Yes' : 'No'}</div>
      <button onClick={increment}>Clicks: {count}</button>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>

<div id="root"></div>


推荐阅读