effect完全指南阅读心得


effect完全指南阅读心得

render


// 每次渲染都有它的自己的props和state。

/** 就像下面这样,每次节点和函数拿到的值都是渲染的props和state
&
* 更新状态时,React会重新更新组件,每次渲染都能拿到独立的状态
&
* 这个状态在此次渲染中是一个常量
& 
* 每次渲染都有它自己的事件处理函数
*/
// During first render  // Dan的事例
function Counter() {
  const count = 0; // Returned by useState()
  // ...
  <p>You clicked {count} times</p>
  // ...
}

// After a click, our function is called again
function Counter() {
  const count = 1; // Returned by useState()
  // ...
  <p>You clicked {count} times</p>
  // ...
}


// 在状态为3时点击alert ,再讲将状态加到5,几秒后alert的是3 

// 这是因为我们在渲染值为3的那次渲染中触发handleAlertClick这个函数了

// Dan 说,每一次渲染都有一个“新版本”的handleAlertClick。每一个版本的handleAlertClick“记住” 了它自己的 count


// 在任意一次渲染中,props和state是始终保持不变的。

// 如果props和state在不同的渲染中是相互独立的,那么使用到它们的任何值也是独立的(包括事件处理函数)。

// 它们都“属于”一次特定的渲染。即便是事件处理中的异步函数调用“看到”的也是这次渲染中的count值。
function Counter() {
  const [count, setCount] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

每次渲染都有它自己的effect


/**
* effect 函数在每次渲染中都不相同
& 
* 每一个effect看到的状态都它所属的那次渲染
& 
* effect会在每次更改dom并让浏览器绘制屏幕后去执行 (render后)
&
* 每次渲染都有它自己的所有
& 
* effect 清除函数会在下一次渲染完成之后执行,再运行下一次渲染的effect函数
&
* React.Hooks非常依赖JavaScript闭包
*/

/**
* 在刚刚set完一个状态的函数里,立即log这个改变的值,
& 
* 你会发现它 "不是最新的 " ,
&
* 因为打印的依然是上一次的值,这时你认为useState hook发生了异步。
& 
* 实际上并不是这样,
& 
* 如果你在其他地方打印,值一定是没有问题的,
& 
* 你只是不能在那个set完的函数里拿到最新的值,
& 
* 这是因为你在上一次的渲染函数里希望看到未来的state或props。
*/

使用effect依赖项数组来决定是否执行effect

// effect中使用到的 状态 和 props 以及函数,都要放入依赖项数组,

// 这样effect就能确保这些数据和函数发生变化时,同步数据到页面,

// 而如果依赖项没有改变,那么就不会执行effect

对依赖项数组用到的state 、props 和函数用来就一定要写进去

 // 有这样一个误解,就是函数不需要依赖项
 
 // 记住,即使是函数,依然也需要依赖项

 function SearchResults() {
  // Imagine this function is long
  function getFetchUrl() {
    return 'https://hn.algolia.com/api/v1/search?query=react';
  }

  // Imagine this function is also long
  async function fetchData() {
    const result = await axios(getFetchUrl());
    setData(result.data);
  }

  useEffect(() => {
    fetchData();
  }, []);
     
 /**
  * Dan 说
  * 需要明确的是,上面的代码可以正常工作。
  * 但这样做在组件日渐复杂的迭代过程中我们很难确保它在各种情况下还能正常运行。
 */
     
  
/**
 * 虽然在effect里放一个函数,然后空数组,这样可以正常执行,
 &
 * 但随着组件的迭代,代码量的增加,甚至我们对函数进行了抽离(将抽离的更多函数放于effect外面,就想以上的代码),
 &
 * 甚至在一些函数中使用了state, ( 而像这种情况依赖项数组并不会发出警告!)
 &
 * 但若是我们没有在effect里设置这些函数的依赖项,那么effect就不会更新最新的props和state,
 &
 * 所以这是一种隐患
*/

 /**
 * 所以如果某些函数仅在effect中调用, 你可以把这些函数移入effect中,
 &
 * 这是一个简单的解决方案,Dan如是说
 &
 * 这样做有什么好处呢,把仅仅effect用到的函数移入effect,
 &
 * 这样你不传函数用到的相关依赖项,effect的[]就会给出提示,
 &
 * 而不是像即使放在外面,也不会报缺少依赖项这样的问题!
*/

/* effect的设计意图就是要强迫你去关注数据流的改变,
 &
 * 然后决定effect如何去与react同步,
 &
 * 而不是让我们的用户遇到了bug
*/ 

/** 但如果你不想或也不能把代码放入effect(你想要逻辑复用),
 &
 * 但不管怎么复用,都不能对effect依赖项撒谎,
 &
 *  ***用了组件内的state或props就必须写入依赖项!
*/

/**
 * 如果抽离的函数没有用到组件内的任何值,你应该把它提到外面去,
 &
 * 然后就可以自由的在effect里使用, 就像下面的代码
*/


// ✅ Not affected by the data flow
function getFetchUrl(query) {
  return 'https://hn.algolia.com/api/v1/search?query=' + query;
}

function SearchResults() {
  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, []); // ✅ Deps are OK

  useEffect(() => {
    const url = getFetchUrl('redux');
    // ... Fetch data and do something ...
  }, []); // ✅ Deps are OK

  // ...
}
// 另一种写法

function SearchResults() {
  // ✅ Preserves identity when its own deps are the same 
  const getFetchUrl = useCallback((query) => {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }, []);  // ✅ Callback deps are OK 也不会报警告⚠️,因为没有依赖组件的值!,值是函数传参传入的

  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, [getFetchUrl]); // ✅ Effect deps are OK

  useEffect(() => {
    const url = getFetchUrl('redux');
    // ... Fetch data and do something ...
  }, [getFetchUrl]); // ✅ Effect deps are OK

  // ...
}


/**
 * 更加灵活的写法,使用useCallback。可以使用state或props
 * 一旦query改变 getFetchUrl也会改变, 因此会重新走effect,将数据同步
*/


function SearchResults() {
  const [query, setQuery] = useState('react');

  // ✅ Preserves identity until query changes
  const getFetchUrl = useCallback(() => {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }, [query]);  // ✅ Callback deps are OK

  useEffect(() => {
    const url = getFetchUrl();
    // ... Fetch data and do something ...
  }, [getFetchUrl]); // ✅ Effect deps are OK

  // ...
 

参考资料:https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/


文章作者: KarlFranz
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 reprint policy. If reproduced, please indicate source KarlFranz !
评论
  目录