react Hooks初体验   一

171 阅读4分钟

为什么使用 hook

使用Hooks可以让你在不适用类的情况下使用状态和其他的一些react功能。未使用hook之前,react存在的一些问题:

组件之间重复使用有状态逻辑很困难:

React没有提供将可重用行为“附加”到组件的方法(例如,将其连接到store), 使用Hook,你可以从组件中提取有状态逻辑,以便可以独立测试并重用。Hook允许您在不更改组件层次结构的情况下重用有状态逻辑。 这样可以轻松地在许多组件之间或与社区共享Hook

复杂的组件变得难以理解

组件可能会在componentDidMount和componentDidUpdate中执行一些数据提取。并且一些在componentDidMount 中的时间侦听器等需要在 componentWillUnmount 中执行清理,在许多情况下,不可能将这些组件分解为较小的组件,因为状态逻辑遍布整个地方。

为了解决这个问题,Hooks允许根据相关的部分(例如设置订阅或获取数据)将一个组件拆分为较小的函数, 而不是基于生命周期方法强制拆分。你还可以选择使用reducer管理组件的本地状态,以使其更具可预测性。

使用class类,并且类中含有大量的state 和props 自上而下的数据流,使的组件的可复用性降低

但是Hooks允许你在没有类的情况下使用更多React的功能。Hooks 使react使用function函数式编程,减少state,并提高组件的可复用性

基础HooK

一、useState

useState 返回一个 state 和一个更新state的方法setState,调用useState传入的数据是 返回的state的初始值 setState 返回一个更新后的state,每一次调用setState 返回的都是最新的 state值

 function Counter({initialCount}) {
 // 传入初始值,作为 state
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}

setState函数接收的参数为,上一步的state,即当前的state,返回新的state

二、useEffect

1.该 Hook 接收一个包含命令式、且可能有副作用代码的函数。

2.赋值给 useEffect 的函数会在组件渲染到屏幕之后执行.

3.默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候才执行

function Counter({initialCount}) {
 // 传入初始值,作为 state
  const [count, setCount] = useState(initialCount);
  
  useEffect(() => {
    const subscription = props.source.subscribe();
    document.title = `当前文档的标题是:${count}`
  });
  
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}

如果没有useEffect,我们需要在componentDidMount 和componentDidUpdate 里执行上述useEffect里的代码,并且如果需要卸载组件时卸载某些函数,那还需要在componentWillUnmount 里执行要卸载的函数,看,代码量是不是增加了很多,使用useEffect 在componentDidMount 和componentDidUpdate里执行的代码就可以写在useEffect里

如果要清除订阅或者函数呢? 只需要在useEffect函数里返回清除函数来清理

 useEffect(() => {
    const subscription = props.source.subscribe();
    return () => {
        // 清除订阅
        subscription.unsubscribe();
    };
   });
   

为防止内存泄漏,清除函数会在组件卸载前执行。另外,如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除。

effect的条件执行

默认情况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被重新创建。

如果我们不需要在每次组件更新时都创建新的订阅,而是仅需要在 source props 改变时重新创建。 要实现这一点,可以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组。第二个参数来告诉react只有当这个参数的值发生改变时,才执行我们传的副作用函数(第一个参数),更新后的示例如下:

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

三、useReducer

useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。 useReducer 接收的参数为: 自定义的reducer, 初始值, 返回一个有初始值的state 和改变reducer'的方法 action(dispatch)

const [state, dispatch] = useReducer(reducer, initialArg, init);

重写上一节的count例子:

const initState={count:0}

function changeReducer (state,action){
    const {type} = action
    switch(type){
        case 'add':
         return {count: state.count + 1}
        case 'reduce':
         return {count: state.count - 1}
        default:
         return state
    }
}

function counter(){
    const [countState, dispatch] = useReducer(changeReducer,initState)
    return (
    <>
      Count: {countState.count}
      <button onClick={() => dispatch({type:'reset'})}>Reset</button>
      <button onClick={() => dispatch({type:'add'})}>+</button>
      <button onClick={() => dispatch({type:'reduce'})}>-</button>
    </>
    )
}

四、useRef

const refContainer = useRef(initialValue);

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}