React hooks 的尝试使用

3,378 阅读3分钟

React Hooks出来也已经挺久了,一直苦于没有机会去尝试。这次不用不知道,一用吓一跳,用hooks写页面和组件真的是太爽啦!再也不用写冗余的类,不用维护复杂的state和考虑异步的setState,不用考虑this的指向,不用细想在哪个生命周期里执行复杂的逻辑,写起来只能用行云流水来形容。如果你还在犹豫要不要使用hooks,请大胆的去尝鲜吧!(p.s:记得把react升级到16.8版本)


这里就不介绍hooks是什么,只是记录了一些我在项目里hooks的一些用法。如果想详细了解hooks的话,我推荐去看官方文档和一些入门文章

传送门:
React-文档 Hook 简介
30分钟精通React Hooks
React16:Hooks总览,拥抱函数式 (这大概是最全的React Hooks吧)


Hooks只能用在纯函数组件里

以前组件通信,要么使用props一层层传递下去,要么使用Redux等状态管理架构。这些方法冗余,繁琐,易出错。

Hooks的出现,带给了我们新的解决这些问题的方法。

1.使用useContext实现组件间通信

import React, { createContext, useContext } from 'react';

// 创建Context  
const Context = createContext(defaultContext);

const shareContext = {
    name: 'context',
    value: 12,
};

// 顶层组件提供共享值C
<Context.Provider value={shareContext} >
    <ChildrenComponents />
</Context.Provider>

// 子组件通过useContext,和 Context 获取共享值
function ChildrenComponents() {
    const { name, value } = useContext(Context);
    console.log(name); // 'context'
    console.log(value); // 12
}

2. 使用useReducer管理多个组件之间的状态

useReducer的基本概念与Redux一致,不了解的可以先去了解一下什么是action,dispatch,reducer之类的。

// actions
const SOME_ACTIONS = 'SOME_ACTIONS';   // 定义action

// reducer
function reducer(state, action) {
    switch(action.type) {
        case SOME_ACTIONS:
            /*handle state with action.payload*/
            return nextState;
        default:
            return state;
    }
}

const iniState = /*初始状态*/

// 组件里使用useReducer

fuction Components() {
    const [state, dispatch] = useReducer(reducer, iniState);
    
    /* state 即为所共享的状态*/
    
    /* 使用 dispatch 通过指定 action 修改 state 的值*/
    
    dispatch({ type: ACTION_TYPE, payload: SOME_PROPS });
}

3. 进阶版:redux hooks: useSelector, useDispatch

React Redux Hooks

const store = createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

import { useSelector, useDispatch, useStore  } from 'react-redux'

export const CounterComponent = () => {
  const counter = useSelector(state => state.counter);
  const dispatch = useDispatch();
  const store = useStore(); // 获取整个store,尽量少用

  return (
    <div>
      <span>{counter}</span>
      <button onClick={() => dispatch({ type: 'increment-counter' })}>
        Increment counter
      </button>
    </div>
  )
}

4.实现一个自定义hooks

我们可以根据需求设计自定义hooks。注意,自定义hooks一定要以useXXX的格式命名。具体原因可到官方文档去了解:Building Your Own Hooks

4.1 实现一个即插即用弹窗(基于antd Modal组件)

以前我们使用Modal组件,总是要自己维护一套state来控制Modal的visible,onCancel等设计比较固定的属性,带来冗余代码和形态各异的实现。而且需要提前引入组件,这会造成某些奇怪的问题和不必要的渲染。

// 控制Modal生命周期的hooks:

// 组件的唯一id, 包裹组件的div, 组件props,回调函数
function useModal(id = '__YOU_SHOULD_PROVIDE_AN_ID', WrapDiv, dataProps) {
  const [visible, setVisible] = useState(false);

  function showModal() {
    setVisible(true);
  }

  function destroy() {
    const unmountResult = ReactDOM.unmountComponentAtNode(WrapDiv);
    if (unmountResult && WrapDiv.parentNode) {
      WrapDiv.parentNode.removeChild(WrapDiv);
    }
  }

  function closeModal() {
    setVisible(false);
    destroy();
  }

  return [showModal, {
    key: id,
    visible,
    onCancel: closeModal,
    ...dataProps,
  }];
}

function DirectiveModal({ DirectModal, id, WrapDiv, ...props }) {
  const [showModal, ModalProps] = useModal(id, WrapDiv, props);

  useEffect(() => {
    showModal();
  }, []);

  return (
    <DirectModal {...ModalProps} />
  );
}

DirectiveModal.propTypes = {
  id: PropTypes.string,
  DirectModal: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
  WrapDiv: PropTypes.object,
  props: PropTypes.any,
  callback: PropTypes.func,
};

function createDirectiveModal(DirectModal, props) {
  const WrapDiv = document.createElement('div');
  document.querySelector('#app').appendChild(WrapDiv);

  function render() {
    ReactDOM.render(
      <DirectiveModal
        DirectModal={DirectModal}
        WrapDiv={WrapDiv}
        {...props}
      />,
      WrapDiv
    );
  }

  render();
}

export default createDirectiveModal;

4. 总结

灵活组合使用hooks能使我们编程更便利,代码逻辑更加清晰高效。自定义hooks带来的可拓展性更是潜力无穷。也难怪当初hooks出来的时候引爆互联网了。心动的快赶快把你的React升级到16.8,去体验hooks的无穷魅力吧!