王义云 發表於 2020-3-18 01:11:00

React hooks详解

<p>此篇文章仅是对hooks入门的总结,老鸟略过吧~</p>
<p>React从16.8.X以后增加了一个新特性,react hooks 让我们看看这个新特性又带来了哪些惊喜呢~以下内容我们采取不同方式创建组件来进行对比总结</p>
<p><strong>组件的创建方式:</strong></p>
<p>用过react的都了解,传统react创建组件提供了两种方式,函数式与类(class)</p>
<p><strong>class创建无状态组件</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
    super(props);
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
      &lt;p&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}&lt;/p&gt;
    &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">}
}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> renderApp() {
let appProps </span>=<span style="color: rgba(0, 0, 0, 1)"> {
    name: </span>'dqhan'<span style="color: rgba(0, 0, 0, 1)">
}
ReactDOM.render(
    </span>&lt;App{...appProps} /&gt;,
    document.getElementById('app'<span style="color: rgba(0, 0, 0, 1)">)
)
}

renderApp();</span></pre>
</div>
<p><strong>添加状态管理</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      name: props.name
    };
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.handleChangeName = <span style="color: rgba(0, 0, 255, 1)">this</span>.handleChangeName.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}

handleChangeName() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
      name: </span>'我变了'<span style="color: rgba(0, 0, 0, 1)">
    })
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;React.Fragment&gt;
      &lt;p&gt; {`hello~${<span style="color: rgba(0, 0, 255, 1)">this</span>.state.name}`} &lt;/p&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleChangeName}&gt;&lt;/button&gt;
      &lt;/React.Fragment&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>我们通过class可以实现无状态组件以及常规组件通过setState的状态管理。</p>
<p><strong>函数式创建组件</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App(props) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;p&gt;{`hello! ${props.name}`}&lt;/p&gt;
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> renderApp() {
let appProps </span>= { name: "dqhan"<span style="color: rgba(0, 0, 0, 1)"> };
ReactDOM.render(</span>&lt;App {...appProps} /&gt;, document.getElementById("app"));
}</pre>
</div>
<p>函数式创建组件通常是无状态组件,这种方式没有办法在内部对状态统一管理,如果我们非要添加状态管理呢,那就只能借助redux啦~或者我们自己利用观察者模式实现一个发布订阅(emmmm比较勉强吧,毕竟实际开发中我们不可能这么做)</p>
<p>那么如果我们非要这么做呢,正题来了,React版本在16.8.X以后增添了一个新特性就是hooks。</p>
<p>hooks涉及API有useState、 useEffect、 useCallback、 useRef、 useMemo、 React.memo、 useReducer等,具体可以参考官方文档,我们来看一下hooks怎么用</p>
<p><strong>React Hooks创建组件</strong></p>
<p><strong>无状态组件</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App(props) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
    &lt;p&gt;{`hello~${props.name}`}&lt;/p&gt;
&lt;/div&gt;
}</pre>
</div>
<p>哈哈,跟函数式一样,毕竟我们要在函数式组件里添加状态管理嘛</p>
<p><strong>1.useState</strong></p>
<p>作用:添加状态管理</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState('dqhan'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
    &lt;p&gt;{`hello~${name}`}&lt;/p&gt;
    &lt;button onClick={() =&gt; setName('我变了')}&gt;Click&lt;/button&gt;
&lt;/div&gt;;
}</pre>
</div>
<p>react&nbsp;hook可以管理自己的状态,有自己的函数钩子,这点相比要函数式显然效果更好,不需要借助redux,这就是我们为啥要在前面提到函数式编程涉及状态管理问题,就是要在这里跟react&nbsp;hook做个比较。</p>
<p>到这里我们知道了这个useState,多了一个useState让函数式创建类有了自己的持久状态。那么在函数式里面我们如何做到class组件中的setState呢?</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App(props) {
let </span>= useState('dqhan'<span style="color: rgba(0, 0, 0, 1)">);
let handleChangeName </span>= useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    setName(preState </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      let updatedValues </span>=<span style="color: rgba(0, 0, 0, 1)"> {
      newValue: </span>'我变了'<span style="color: rgba(0, 0, 0, 1)">
      }
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { ...{ preState }, ...updatedValues }
    })
})
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> click1(params) {
    setName(</span>'我变了1'<span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
    &lt;p&gt;{`hello~${name}`}&lt;/p&gt;
    &lt;button onClick={handleChangeName}&gt;Click1&lt;/button&gt;
    &lt;button onClick={click1}&gt;Click2&lt;/button&gt;
&lt;/div&gt;;
}</pre>
</div>
<p>这中方式已经实现了状态整合,但是我们如果模拟一个state呢,来统一管理state呢,我们可以这么实现</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App(props) {
let </span>=<span style="color: rgba(0, 0, 0, 1)"> useState({
    name: </span>'dqhan'<span style="color: rgba(0, 0, 0, 1)">
});
let handleChangeName </span>= useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    setState(preState </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      let updatedValues </span>=<span style="color: rgba(0, 0, 0, 1)"> {
      name: </span>'我变了'<span style="color: rgba(0, 0, 0, 1)">
      }
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { ...preState, ...updatedValues }
    })
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
    &lt;p&gt;{`hello~${state.name}`}&lt;/p&gt;
    &lt;button onClick={handleChangeName}&gt;Click&lt;/button&gt;
&lt;/div&gt;;
}</pre>
</div>
<p>到目前为止,已经知道了react&nbsp;hook中如何使用state,那么周期函数呢,那么就涉及另一个钩子useEffect</p>
<p><strong>2.useEffect</strong></p>
<p>作用:周期函数</p>
<p>useEffect涉及三个周期函数 componentDidMount 、componentDidUpdate、 compinentWillUmount&nbsp;我们看一个简单的实例</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
</span><span style="color: rgba(0, 0, 255, 1)">var</span> = useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(`update</span>--<span style="color: rgba(0, 0, 0, 1)">${count}`);
}, );
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>&lt;div&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Click&lt;/button&gt;
    &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<div>下面我们来了解一下react&nbsp;hooks的周期函数,他是如何工作的</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(
    () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">默认每一次渲染都需要执行的方法</span>
      console.log('didmount'<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果需要实现componentWillComponent,则return 一个函数即可    </span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> unmount() {
      console.log(</span>'unmount'<span style="color: rgba(0, 0, 0, 1)">)
      }
    }
)

let handleSetCount </span>= useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    setCount((preCount) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      let updatedCount </span>= preCount + 1<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> updatedCount;
    });
})

</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;React.Fragment&gt;<span style="color: rgba(0, 0, 0, 1)">
    {console.log(</span>'render'<span style="color: rgba(0, 0, 0, 1)">)}
    </span>&lt;p&gt;{`${count}`}&lt;/p&gt;
    &lt;button onClick={handleSetCount}&gt;Click&lt;/button&gt;
&lt;/React.Fragment&gt;
}</pre>
</div>
<div>
<p>我们可以看一下执行周期</p>
<p>第一次渲染时候执行&nbsp;render&nbsp;&nbsp;didmount</p>
<p>点击事件执行顺序&nbsp;render&nbsp;unmount&nbsp;didmount</p>
<p>不难看出,每一次渲染我们都会执行render进行渲染,然后清除掉上一次的useEffect,然后渲染完成之后重新执行useEffect</p>
<p>这样通过一个useEffec可以默认执行两个周期函数,也就是当我们需要对组件添加一些需要当组件卸载时候清除掉的功能时候,这个是很方便的,常见的就是setTimeout&nbsp;setIntrval等定时器</p>
<p>但是比如一个component渲染之后我们通常会发送一个请求来请求数据,然后重写渲染这个组件,这样会造成死循环怎么办,我们可以在useEffect后添加第二个参数</p>
<div>
<p><strong>阻止useEffect每一次都要执行</strong></p>
</div>
</div>
</div>
</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(
    () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">默认每一次渲染都需要执行的方法</span>
      console.log('didmount'<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果需要实现componentWillComponent,则return 一个函数即可    </span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> unmount() {
      console.log(</span>'unmount'<span style="color: rgba(0, 0, 0, 1)">)
      }
    },
   
)

let handleSetCount </span>= useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    setCount((preCount) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      let updatedCount </span>= preCount + 1<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> updatedCount;
    });
})

</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;React.Fragment&gt;<span style="color: rgba(0, 0, 0, 1)">
    {console.log(</span>'render'<span style="color: rgba(0, 0, 0, 1)">)}
    </span>&lt;p&gt;{`${count}`}&lt;/p&gt;
    &lt;button onClick={handleSetCount}&gt;Click&lt;/button&gt;
&lt;/React.Fragment&gt;
}</pre>
</div>
<p>当传入第二个参数得值不变得时候就会跳过useEffect函数执行</p>
<div>
<p>如何模拟componentDidMount与componentWillUmount,第二个参数我们传一个空数组,这样就可以实现仅当组件渲染跟组件卸载得时候执行</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(
    () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">默认每一次渲染都需要执行的方法</span>
      console.log('didmount'<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果需要实现componentWillComponent,则return 一个函数即可    </span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> unmount() {
      console.log(</span>'unmount'<span style="color: rgba(0, 0, 0, 1)">)
      }
    },
    []
)

let handleSetCount </span>= useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    setCount((preCount) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      let updatedCount </span>= preCount + 1<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> updatedCount;
    });
})

</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;React.Fragment&gt;<span style="color: rgba(0, 0, 0, 1)">
    {console.log(</span>'render'<span style="color: rgba(0, 0, 0, 1)">)}
    </span>&lt;p&gt;{`${count}`}&lt;/p&gt;
    &lt;button onClick={handleSetCount}&gt;Click&lt;/button&gt;
&lt;/React.Fragment&gt;
}</pre>
</div>
<div>
<p>不过,这隐藏了一个问题:传递空数组容易出现问题。如果咱们添加了依赖项,那么很容易忘记向其中添加项,如果错过了一个依赖项,那么该值将在下一次运行useEffect时失效,并且可能会导致一些奇怪的问题。</p>
<p>常见得就是当我们想用父组件调用子组件时候使用得ref,或者我们要获取dom焦点时</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
let </span>= useState(''<span style="color: rgba(0, 0, 0, 1)">)
const inputRef </span>=<span style="color: rgba(0, 0, 0, 1)"> useRef();

useEffect(
    () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">默认每一次渲染都需要执行的方法</span>
      console.log('didmount'<span style="color: rgba(0, 0, 0, 1)">)

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果需要实现componentWillComponent,则return 一个函数即可    </span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> unmount() {
      console.log(</span>'unmount'<span style="color: rgba(0, 0, 0, 1)">)
      }
    },
   
)

let handleSetCount </span>= useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    setCount((preCount) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      let updatedCount </span>= preCount + 1<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> updatedCount;
    });
})

let handleSetValue </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (e) {
    setValue(e.target.value);
}

</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;React.Fragment&gt;<span style="color: rgba(0, 0, 0, 1)">
    {console.log(</span>'render'<span style="color: rgba(0, 0, 0, 1)">)}
    </span>&lt;p&gt;{`${count}`}&lt;/p&gt;
    &lt;<span style="color: rgba(0, 0, 0, 1)">input
      ref</span>=<span style="color: rgba(0, 0, 0, 1)">{inputRef}
      value</span>=<span style="color: rgba(0, 0, 0, 1)">{value}
      onChange</span>=<span style="color: rgba(0, 0, 0, 1)">{handleSetValue}
    </span>&gt;&lt;/input&gt;
    &lt;<span style="color: rgba(0, 0, 0, 1)">button
      ref</span>=<span style="color: rgba(0, 0, 0, 1)">{inputRef}
      onClick</span>=<span style="color: rgba(0, 0, 0, 1)">{handleSetCount}
    </span>&gt;Click&lt;/button&gt;

&lt;/React.Fragment&gt;
}</pre>
</div>
<p>下面我们实现一个请求实例</p>
</div>
<p><strong>请求实例</strong></p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
useEffect(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    const fetchData </span>= async () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      const result </span>=<span style="color: rgba(0, 0, 0, 1)"> await axios(config);
      setData(result);
    };

    fetchData();
}, []);

</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;&lt;/div&gt;;
}</pre>
</div>
<p><strong>利用hook可以做到分离接口</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useFetchHook(config, watch) {
let </span>= useState(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(
    () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      const fetchData </span>= async () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
          const result </span>=<span style="color: rgba(0, 0, 0, 1)"> await axios(config);
          setData(result);
          setStatus(</span>0<span style="color: rgba(0, 0, 0, 1)">);
      } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (e) {
          setStatus(</span>1<span style="color: rgba(0, 0, 0, 1)">);
      }
      fetchData();
      };
    },
    watch </span>?<span style="color: rgba(0, 0, 0, 1)"> [...watch] : []
);

</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { data, status };
}</span></pre>
</div>
<p>现在我们整体上知道了useState useEffect怎么使用了,我们来自己实现一个简易的</p>
<p><strong>实现useState</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> val;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useState(initVal) {
let resultVal </span>= val ||<span style="color: rgba(0, 0, 0, 1)"> initVal;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> setVal(newVal) {
    resultVal </span>=<span style="color: rgba(0, 0, 0, 1)"> newVal;
    render();
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p><strong>实现useEffect</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> watchArr =<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useEffect(fn, watch) {
</span><span style="color: rgba(0, 0, 255, 1)">var</span> hasWatchChange = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
hasWatchChange </span>= watchArr &amp;&amp; watch.every((val, i) =&gt; val ===<span style="color: rgba(0, 0, 0, 1)"> watchArr)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (hasWatchChange) {
    fn();
    watchArr </span>=<span style="color: rgba(0, 0, 0, 1)"> watch;
}
}</span></pre>
</div>
<p>hooks里面最常用的两个API就是useState与useEffect,现在是不是已经了解了呢,下面我们介绍一些其他API</p>
<p><strong>3.useContext</strong></p>
<div>
<p>作用:越级别获取组件内容</p>
<p>类组件中我们也常用context,类组件实现方式</p>
<div>
<div class="cnblogs_code">
<pre></pre>
<div> const&nbsp;AppContext&nbsp;=&nbsp;React.createContext('target');</div>
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
    super(props)
}
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;AppContext.Provider value="dark"&gt;
      &lt;Target /&gt;
      &lt;/AppContext.Provider&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

class Target extends React.Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过定义静态属性 contextType 来订阅</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">没有定义是获取不到的</span>
static contextType =<span style="color: rgba(0, 0, 0, 1)"> AppContext;
render() {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.context);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;&lt;/div&gt;;
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p><strong>Hooks实现方式</strong></p>
<div class="cnblogs_code">
<pre>const AppContext = React.createContext('target'<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
useEffect(
    () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> { },
    []
);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;AppContext.Provider value="dark"&gt;
    &lt;Target /&gt;
&lt;/AppContext.Provider&gt;;
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Target() {
const value </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(AppContext);
console.log(value);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;&lt;/div&gt;;
}</pre>
</div>
<p>在需要订阅多个&nbsp;context&nbsp;的时候,就更能体现出useContext的优势。</p>
<p><strong>传统的实现方式</strong></p>
</div>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;CurrentUser.Consumer&gt;<span style="color: rgba(0, 0, 0, 1)">
    {
      user </span>=&gt; &lt;Notifications.Consumer&gt;<span style="color: rgba(0, 0, 0, 1)">
      {notifications </span>=&gt;
          &lt;header&gt;<span style="color: rgba(0, 0, 0, 1)">
            Welcome back, {user.name}</span>!<span style="color: rgba(0, 0, 0, 1)">
            You have {notifications.length} notifications.
          </span>&lt;/header&gt;
<span style="color: rgba(0, 0, 0, 1)">      }
      </span>&lt;/Notifications.Consumer&gt;
<span style="color: rgba(0, 0, 0, 1)">    }
</span>&lt;/CurrentUser.Consumer&gt;
}</pre>
</div>
<p><strong>hooks实现</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
const user </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(CurrentUser);
const notifications </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(Notifications);

</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>&lt;header&gt;<span style="color: rgba(0, 0, 0, 1)">
      Welcome back, {user.name}</span>!<span style="color: rgba(0, 0, 0, 1)">
      You have {notifications.length} notifications.
    </span>&lt;/header&gt;
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>是不是比传统的要简单的多</p>
<div>
<p><strong>4.useReducer</strong></p>
<p>作用:复杂状态管理,跟redux本质上是一样的</p>
<div>
<p>函数式组件如果涉及到状态管理,我们需要借助redux,那么hooks需要吗,答案也是一样的,简单的状态管理我们可以通过useState来进行管理,如果比较复杂的状态管理呢,react&nbsp;hook给我们提供了方法&nbsp;useReducer</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> init(initialCount) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { count: initialCount };
}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> reducer(state, action) {
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (action.type) {
    </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'increment'<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> { count: state.count + 1<span style="color: rgba(0, 0, 0, 1)"> };
    </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'decrement'<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> { count: state.count - 1<span style="color: rgba(0, 0, 0, 1)"> };
    </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'reset'<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> init(action.payload);
    </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Error();
}
}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter({ initialCount }) {
const </span>=<span style="color: rgba(0, 0, 0, 1)"> useReducer(reducer, initialCount, init);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">
      Count: {state.count}
      </span>&lt;<span style="color: rgba(0, 0, 0, 1)">button
      onClick</span>={() =&gt; dispatch({ type: 'reset', payload: initialCount })}&gt;<span style="color: rgba(0, 0, 0, 1)">
      Reset
      </span>&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({ type: 'increment' })}&gt;+&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({ type: 'decrement' })}&gt;-&lt;/button&gt;
    &lt;/&gt;
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p><strong>5.useCallback</strong></p>
<p>作用:提升性能,缓存事件,减少没必要的渲染</p>
<p>当我们使用类组件创建时,我们会怎么绑定事件呢</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
    super(props);
}
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
      &lt;p&gt;{`hello~${name}`}&lt;/p&gt;
      &lt;button onClick={() =&gt; { console.log('click') }}&gt;Click&lt;/button&gt;
    &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>这样写会导致什么结果呢,就是当渲染的时候react会认为每一次绑定的事件都是新的,从而从新进行计算</p>
<p>改进如下</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick = <span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
handleClick() {
    console.log(</span>'click'<span style="color: rgba(0, 0, 0, 1)">)
}
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
      &lt;p&gt;{`hello~${name}`}&lt;/p&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;Click&lt;/button&gt;
    &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>我们讲触发函数绑定在this上,来缓存这个方法</p>
<p><strong>hooks</strong></p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
    &lt;button onClick={() =&gt; setCount(1)} &gt;&lt;/button&gt;
&lt;/div&gt;
}</pre>
</div>
<p>同样的问题这么写也是存在的,改进如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
let handleSetCount </span>= useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    setCount(</span>1<span style="color: rgba(0, 0, 0, 1)">);
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;
    &lt;button onClick={handleSetCount} &gt;&lt;/button&gt;
&lt;/div&gt;
}</pre>
</div>
<p>我们通过useCallback来缓存这个事件达到优化效果</p>
<div>
<p><strong>6.useMemo</strong></p>
<p>作用:提升性能,选择性的渲染变化组件</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App(target, target2) {
const target </span>= useMemo(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;Target /&gt;
<span style="color: rgba(0, 0, 0, 1)">}, )
const target2 </span>= useMemo(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;Target2 /&gt;
<span style="color: rgba(0, 0, 0, 1)">}, )
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
    {target}
    {target2}
</span>&lt;/div&gt;
}</pre>
</div>
<p>当target变化仅渲染Target组件,同理也作用与Target2组件</p>
<p><strong>React.memo</strong></p>
<p>作用:提升性能</p>
<div>
<p>如果想实现class中的shouldComponentUpdate方法呢&nbsp;,区别是它只能比较&nbsp;props,不会比较&nbsp;state:</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="cnblogs_code">
<pre>const App = React.mome((target, target2) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
const target </span>= useMemo(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;Target /&gt;
<span style="color: rgba(0, 0, 0, 1)">}, )
const target2 </span>= useMemo(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;Target2 /&gt;
<span style="color: rgba(0, 0, 0, 1)">}, )
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
    {target}
    {target2}
</span>&lt;/div&gt;
})</pre>
</div>
<p><strong>7.useRef</strong></p>
<p>作用:获取dom依赖关系</p>
<p>类组件实现方式</p>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.myRef =<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
}

componentDidMount() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.myRef.current.focus();
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;input ref={<span style="color: rgba(0, 0, 255, 1)">this</span>.myRef} type="text" /&gt;;
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>hooks</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(''<span style="color: rgba(0, 0, 0, 1)">)
const inputRef </span>=<span style="color: rgba(0, 0, 0, 1)"> useRef();

useEffect(
    () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    },
   
)

let handleSetValue </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (e) {
    setValue(e.target.value);
}

</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;React.Fragment&gt;
    &lt;<span style="color: rgba(0, 0, 0, 1)">input
      ref</span>=<span style="color: rgba(0, 0, 0, 1)">{inputRef}
      value</span>=<span style="color: rgba(0, 0, 0, 1)">{value}
      onChange</span>=<span style="color: rgba(0, 0, 0, 1)">{handleSetValue}
    </span>&gt;&lt;/input&gt;

&lt;/React.Fragment&gt;
}</pre>
</div>
<p>好了hooks的基本使用方式就介绍完了,现在你对hook多少能了解了一些吧~</p>
<p>代码地址:https://github.com/Dqhan/React</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div>&nbsp;</div>
</div>
<div>&nbsp;</div>
<div>
<div>
<div>
<p>&nbsp;</p>
</div>
</div>
<div>&nbsp;</div>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/moran1992/p/12514899.html
頁: [1]
查看完整版本: React hooks详解