Skip to content

精读《Function Component 入门》 #157

Closed
@ascoders

Description

@ascoders
Owner

本周的精读依然没有文章,由笔者开发经验总结而来。

通过笔者对 Function Component 的开发经验进行分享,内容由浅入深,偏入门型。


精读《Function Component 入门》

Activity

Kavelaa

Kavelaa commented on Oct 16, 2019

@Kavelaa

你好,这个地方的例子我感觉不太严谨

https://github.com/dt-fe/weekly/blob/v2/104.%E7%B2%BE%E8%AF%BB%E3%80%8AFunction%20Component%20%E5%85%A5%E9%97%A8%E3%80%8B.md#%E5%B0%86%E5%87%BD%E6%95%B0%E6%8A%BD%E5%88%B0%E7%BB%84%E4%BB%B6%E5%A4%96%E9%83%A8

换一个例子就可以看得更清楚:

function Parent() {
const [count, setCount] = useState(0);
const [step, setStep] = useState(0);
const [other, setOther] = useState(0);
const drag = useDraggable(count, step); // 封装了拖拽函数
}
假设我们使用 Sortablejs 对某个区域进行拖拽监听,这个函数每次都重复执行的性能损耗非常大,然而这个函数内部可能因为仅仅要上报一些日志,所以依赖了没有实际被使用的 count step 变量:

function useDraggable(count, step) {
return useCallback(() => {
// 上报日志
report(count, step);

// 对区域进行初始化,非常耗时
// ... 省略耗时代码
}, [count, step]);
}
这种情况,函数的依赖就特别不合理。虽然依赖变化应该触发函数重新执行,但如果函数重新执行的成本非常高,而依赖只是可有可无的点缀,得不偿失。

其中在useDraggableconst drag = ...这里,笔者想表达的意思应该是说,当Parent组件中有一个useEffect且将drag作为依赖项的时候,因为依赖了没有实际价值的countstep频繁的变化导致drag频繁变化,最终导致副作用被频繁地触发,而drag函数中涉及了高开支的计算,这里对性能造成了很大的影响。
而当我第一次看见这里的时候,容易让我理解成传入useCallback的函数被立即执行了,因为在Parent函数里边写到const drag = ...之后就没下文了,从而带给我一种感觉就是useDraggable把返回值赋给drag的时候就已经进行计算了,但是实际上是因为drag发生变化,从而触发了useEffect导致drag函数被调用,这个时候才实际进行了高开支计算。

ascoders

ascoders commented on Oct 16, 2019

@ascoders
OwnerAuthor

是的,感谢纠正,drag 没有执行时是没有开销的。

CorgiTT

CorgiTT commented on Dec 7, 2020

@CorgiTT
function useEventCallback(fn, dependencies) {
  const ref = useRef(null);

  useEffect(() => {
    ref.current = fn;
  }, [fn, ...dependencies]);

  return useCallback(() => {
    const fn = ref.current;
    return fn();
  }, [ref]);
}

React 官方不推荐使用此范式,因此对于这种场景,利用 useReducer,将函数通过 dispatch 中调用。 还记得吗?dispatch 是一种可以绕过依赖的黑魔法,我们在 “什么是 useReducer” 小节提到过。

  1. 这里将函数抽离到组件外部场景里,使用dispatch调用外部函数。不知道我的理解是不是正确,大佬请指教下:
//组件外部的自定义hook
function useFetch(count, step) {
  return useCallback(() => {
    const url = "https://v/search?query=" + count + "&step=" + step;
  }, [count, step]);
}

function Parent() {
  const reducer=(state,action)=>{
    switch(action.type){
      case 'useFetch':{
        const fetch = useFetch(state.count,state.step); // 调用外部 useFetch
        fetch()
      }
      case 'setCount':
      ...
      case 'setStep':
      ...
      case 'setOther':
      ...
    }
  }
  const [state, dispatch] = useReducer(reducer,{
    count:0,
    step:0,
    other:0
  });

  useEffect(() => {
    dispatch({type:'useFetch'}); //调用dispatch
  }, [dispatch]);

  return (
    <div>
      <button onClick={dispatch({type:'setCount',...});}>setCount {count}</button>
      <button onClick={dispatch({type:'setStep',...});}>setStep {step}</button>
      <button onClick={dispatch({type:'setOther',...});}>setOther {other}</button>
    </div>
  );
}
  1. 如果如上写法,如何做到监听count、step变化随即调用 dispatch({type:'useFetch'})
  useEffect(() => {
    dispatch({type:'useFetch'}); //调用dispatch
  }, [dispatch]);// 这里需要依赖[dispatch,count,step]变量来触发dispatch吗?
Flying-Snail

Flying-Snail commented on Feb 22, 2021

@Flying-Snail

setTimeout 的例子,三次点击触发了四次渲染,但 setTimeout 分别生效在第 1、2、3 次渲染中,因此值是 0 1 2。
useEffect 的例子中,三次点击也触发了四次渲染,但 useEffect 分别生效在第 1、2、3、4 次渲染中,最终使 currentCount 的值变成 3。

这里不是很理解,为什么三次点击会触发四次渲染呢,每次点击不是只会更改一次 count,一个 count 的更改,不是只触发一次重新渲染么?

ascoders

ascoders commented on Feb 23, 2021

@ascoders
OwnerAuthor

@Flying-Snail 这里说的是一共产生了 4 次渲染闭包。算上初始化的那次,一共 4 次。

suerta-git

suerta-git commented on Apr 14, 2021

@suerta-git

currentValue那里,每一次更新currentValue的值都需要等到渲染完成之后,那在代码执行过程中,currentValue.current不实际是上一次render时候的值而不是本次render的值吗?

newsekaes

newsekaes commented on Aug 15, 2022

@newsekaes

@Flying-Snail 这里说的是一共产生了 4 次渲染闭包。算上初始化的那次,一共 4 次。

是的,一开始读的时候也是觉得不理解,想了片刻才转过弯来

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @ascoders@CorgiTT@newsekaes@Flying-Snail@Kavelaa

        Issue actions

          精读《Function Component 入门》 · Issue #157 · ascoders/weekly