React Hooks 使用技巧

49 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

类组件和函数组件

类组件-容器组件

所谓类组件,就是基于 ES6 Class 这种写法,通过继承 React.Component 得来的 React 组件。以下是一个典型的类组件:

class DemoClass extends React.Component {
  // 初始化类组件的 state
  state = {
    text: ""
  };
  // 编写生命周期方法 didMount
  componentDidMount() {
    // 省略业务逻辑
  }
  // 编写自定义的实例方法
  changeText = (newText) => {
    // 更新 state
    this.setState({
      text: newText
    });
  };
  // 编写生命周期方法 render
  render() {
    return (
      <div className="demoClass">
        <p>{this.state.text}</p>
        <button onClick={this.changeText}>点我修改</button>
      </div>
    );
  }
}

什么是函数组件/无状态组件(Function Component/Stateless Component) 函数组件顾名思义,就是以函数的形态存在的 React 组件。早期并没有 React-Hooks 的加持,函数组件内部无法定义和维护 state,因此它还有一个别名叫“无状态组件”。以下是一个典型的函数组件:

function DemoFunction(props) {
  const { text } = props
  return (
    <div className="demoFunction">
      <p>{`function 组件所接收到的来自外界的文本内容是:[${text}]`}</p>
    </div>
  );
}
函数组件与类组件的对比:无关“优劣”,只谈“不同”

我们先基于上面的两个 Demo,从形态上对两种组件做区分。它们的区别就包括但不限于:

  1. 类组件需要继承 class,函数组件不需要;

  2. 类组件可以访问生命周期方法,函数组件不能;

  3. 类组件中可以获取到实例化后的 this,并基于这个 this 做各种各样的事情,而函数组件不可以;

  4. 类组件中可以定义并维护 state(状态),而函数组件不可以; .....

总结:

  1. class组件使用时,使用了类组件的部分特点(生命周期,等等): a. 封装:将一类属性或者方法聚拢到一个class里; b. 继承: 新增的class组件可以直接继承现有class组件,实现对对上述封装方法的复用。
  2. 函数组件在没有hooks时,虽然不能维护自己的state,但也能完成一些复杂交互,比如交互事件等等,而且相对于类组件更加的简洁灵活。
  3. 最后,函数组件会捕获 render 内部的状态,这是两类组件最大的不同。

参考文章:函数组件和类组件的不同

  1. 函数组件更加契合 React 框架的设计理念。
UI = render(data) state data == props  hooks useState()
UI = f(data)

React本质做的事情是把 数据转为虚拟DOM树,再由不同平台的render去渲染成实际UI,比如H5使用的是ReactDomRender,RN使用的是ReactNativeRender,函数组件更契合React的本质。

函数组件的使用

使用原则:

  1. 不要在函数组件外使用
  2. 不能在循环和判断或者嵌套函数中使用

function f() { return (

) }

function useHooks () {

}

自定义hooks

对于有使用 useState 或者 useEffect 的可以考虑,其余的可以单独拆分 util

deps

问答
  1. 直接放在函数体内,和放在 useEffect 的回调的有什么区别?

函数体是在每次render时就执行,useEffect代表的是副作用,是在render完成以后捕获state或者props再执行。副作用一定是和当前 render 的 结果没关系的,而只是 render 完之后做的一些额外的事情。

const add = p => p+1

useEffect(() => { // todo // 为了捕获当前的state

// state/props/usememo/usecalback

setInterval(() => { // 0 // render 捕获 setCount(add) }, 1000) }, [])

  1. 下面2种写法哪种优化更好?

useCallback 缓存一个函数

const handleIncrement = useCallback(() => setCount(count + 1), [count]);

const handleIncrement = useCallback(() => {
  // ddd
  setCount(q => q + 1)
}, []);

setState(previousState => state)
  1. 不要把 state 当做变量用

我们需要遵循一个原则,即:在保证 State 完整性的同时, 也要保证它的最小化。就是说,某些数据如果能从已有的 State 中计算得到,那么我们就应该始终在用的时候去计算,而不要把计算的结果存到某个 State 中。这样的话,才能简化我们的状态处理逻 辑。

  1. useReducer 应用场景

  2. 受控组件和非受控组件,HCZ搜索之前搜索框的问题。

  3. 条件判断,wrapper

function Modal({visible, ...props}) {
  // 当 visible 为 false时隐藏
  if(!visible) reurn false // 会报错,判断不能影响每次的hooks
  const [state, setState] = useState(0)
  // 这里可能有一大块需要计算的逻辑
  if(!visible) reurn false // 放这里上面的一大块逻辑还是会执行
  return <div></div>
}

// 使用HOC方式包裹
function ModalWrapper ({visible, ...props}) {
  if(!visible) reurn false
  return <Modal {...props}/>
}
  1. render props 就是把一个 render 函数作为属性传递给某个组件,由这个组件去执行这个函数从而 render 实际的内容。render props 适合于UI的复用,hooks适合数据的复用。
// render props 的写法

function CounterRenderProps ({ children }) {
  const [count, setCount] = useState(0)

  const increment = useCallback(() => {
    setCount(count + 1)
  }, [count])

  const decrement = useCallback(() => {
    setCount(count + 1)
  }, [count])

  return children({ count, increment, decrement })
}

// 使用

function CounterRenderPropsExample() {
  return <CounterRenderProps>
    {  ({count, increment, decrement}) => (
      <div>
        <p>counter: {count}</p>
        <div>
          <button onClick={increment}>increment</button>
          <button onClick={decrement}>decrement</button>
        </div>
      </div>
    ) }
  </CounterRenderProps>
}

// 自定义Hooks
const useCounter = (initCounter = 0) {
  const [count, setCount] = useState(initCounter)

  const increment = useCallback(() => {
    setCount(count + 1)
  }, [count])

  const decrement = useCallback(() => {
    setCount(count + 1)
  }, [count])

  return [count, { increment, decrement }]
}

// 自定义hooks的使用
function CounterRenderPropsExample() {
  const [count, { increment, decrement }] = useCounter(0)

  <div>
    <p>counter: {count}</p>
    <div>
      <button onClick={increment}>increment</button>
      <button onClick={decrement}>decrement</button>
    </div>
  </div>
}

hooks 侧重业务的逻辑,自己不生产UI。Hooks 仅能替代纯数据逻辑的 render props。如果有 UI 展示的逻辑需要重用,那么我们还是必须借助于 render props 的逻辑。