「ReactNaitve」对hooks最佳实践的探索

author: 轩邈javascript

1、hooks介绍

一. useState

  1. eg: const [count, setCount] = useState(0)html

  2. 介绍java

    (1)有一个参数:默认值, 能够是函数,只在初始渲染时执行一次node

    (2)返回一个带有两个元素的数组react

    (3)第一个元素是 state 的值, 第二个元素是更新state的函数,每次新的,使用useCallback,可让它使用上次的函数;函数名随意不必定是set某某某,参数能够是要更新的值或者函数,当是函数时,函数参数是上一次state的值。git

(4) 若是有多个state则根据 useState 的调用顺序来“记住”每一个state的状态归属 
 
 (5)这个特性要求Hooks不能够写在if或者switch等可能不执行的代码片断,会致使调用次序不一致
复制代码

二. useLayoutEffect

签名与 useEffect 相同,在DOM变化(layout布局)后同步触发,渲染之(paint绘制)执行 ,适用于用户可见的 DOM 改变。github

三. useEffect

componentDidMountcomponentDidUpdate 不一样,传递给 useEffect 的函数在DOM变化(layout布局) 和渲染(paint绘制)触发。 这使得它适用于许多常见的 side effects ,例如设置订阅和事件处理程序,由于大多数类型的工做不该阻止浏览器更新(判断标准)屏幕。redux

1. eg:
`componentDidMount:  useLayoutEffect(() => {setTitle(1)}, [])`   

  `componentDidUpdate:  useLayoutEffect(() => {console.log(1)})` 

  `componentWillUnmount: useLayoutEffect()=>{return () => {console.log('我要卸载组件啦')}}` 

  我是一个方法,组件更新后返回,下次组件更新前执行:`useLayoutEffect(() => {return () => {console.log(‘我是一个方法,组件更新后返回,下次组件更新前执行')}}, [count, count2])` 
复制代码
2. 介绍:至关于 componentDidMount 、 componentDidUpdate 、componentWillUnmount和 我是一个方法组件更新后返回,下次组件更新前执行
3. 有两个参数
1. 第一个为函数,默认会在渲染完成后执行一次,若是返回的是一个函数,则返回的函数会在第二个参数数组里面的元素发生变化且渲染完成后执行 
  2. 第二个为一个数组(也能够写成常量等类型,不过不会调用参数一),里面写须要监控的state,当state改变时会调用第一个函数,不改变则不会调用参数一,当数组为空时,只会在最开始调用一次,至关于componentDidMount;不传时,默认监控全部state,至关于componentDidUpdate
复制代码

四. useContext

1. eg:
const theme = useContext(ThemeContext) 
复制代码
2. 使用 Contex时
```javascript
  const ThemeContext = React.createContext();
  const LanguageContext = React.createContext();
  ```

  ```js
  <ThemeContext.Consumer>
      {
          theme => (
              <LanguageContext.Cosumer>
                  language => {
                      //可使用theme和lanugage了
                  }
              </LanguageContext.Cosumer>
          )
      }
  </ThemeContext.Consumer>
  ```

  两个render props写法,两个嵌套看起来麻烦不少


  使用useContext时 

  ```js
  const theme = useContext(ThemeContext);
  const language = useContext(LanguageContext);
  // 这里就能够用theme和language了
  ```

  接受一个由React.createContext返回的上下文对象,写法简化不少而且再也不须要理解render props 
复制代码
3. 有时候会形成意想不到的从新渲染
```javascript
  const ThemedPage = () => {
      const theme = useContext(ThemeContext);
      return (
         <div>
              <Header color={theme.color} />
              <Content color={theme.color}/>
              <Footer color={theme.color}/>
         </div>
      );
  };
  ```

  当theme的其余属性(如size等其余非color属性)改变时也会致使界面从新渲染 
复制代码

五. useReducer

1. 介绍:加强版的useState, 小型Redux (机制相似,可是不一样组件内部数据认识独立的,)
2. eg:
```javascript
  const initialState = { count: 0 } 
  const reducer = function reducer(state, action) { 
    switch (action.type) { 
      case 'reset': 
      	return initialState 
      case 'increment': 
      	return { ...state, count: state.count + 1 } 
      case 'decrement': 
      	return { ...state, count: state.count - 1 } 
      default: 
      	return state 
    }
  }
  ```

  //上面这部分应该写在函数组件外面防止函数一遍遍的建立

  const [count3, count3Dispatch] = useReducer(reducer, initialState) 
复制代码
3. 和useState相比dispatch和reducer只被建立了一次

六. useCallback

1. 介绍:返回值为 memoized 回调函数,第一个参数为一个函数,第二个为一个数组,只有内部元素变化,返回的回调函数才会被从新建立
2. eg:
```javascript
  const [count4, setCount4] = useState(0) 
  const counterRef = useRef(count4) 
  
  useEffect(
    () => { 
      counterRef.current = count4 
    }, 
    [count4] 
  )
  
  const incrementCount4 = useCallback(() => setCount4(counterRef.current + 1), []) 
  ```
复制代码
  1. 尽可能少用,通常都能用useReducer优化

七. useMemo

介绍:useCallback(fn, inputs) 等价于 useMemo(() => fn, inputs)小程序

八. useRef

介绍:useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传递的参数(initialValue)。返回的对象将存留在整个组件的生命周期中。react-native

九. useImperativeMethods

  1. useImperativeMethods 自定义使用 ref 时公开给父组件的实例值,方便对函数式组件进行ref操做 使调用内部函数成为可能
  2. 应尽可能避免这种代码

十. 自定义Hooks兴起可造成一种约定,代码之间的公用逻辑可使用useXXX形式的函数

```javascript
// eg:  
const useMountLog = name => {
    useEffect(() => {
        console.log(`${this.name}组件渲染时间->${this.end - this.begin}ms`)
    },[])
}
```
复制代码
// eg: usePrevious 
function usePrevious(value) {
  const ref = useRef()
  useEffect(() => {
    ref.current = value
  })
  return ref.current
}
复制代码

2、环境构建

  1. git clone github.com/facebook/re… (我使用时react版本为16.6.1)
  2. cd react
  3. packages/shared目录下全部文件中的enableHooks = false替换为enableHooks = true
  4. 运行yarn install
  5. 运行yarn build -- --type=RN_OSS
  6. 等运行完毕后,将 build/react-native/ 下的内容替换 项目路径/node_modules/react-native/Libraries/Renderer(我使用时react-native版本为0.57.8)下的内容
  7. 项目中react版本为16.7.0-alpha.2(16.7.0-alpha.0~16.7.0-alpha.2皆可,16.7.0正式版将hooks移除了)

3、原有项目改造

  1. 替换类组件

    // 改造前
    export default class HomeScene extends Component{...}
    // 改造后
    export default forwardRef((props, ref) => {...}) // forwardRef包起来是方便函数组件内部方法调用
    复制代码
  2. 构造函数移除

  3. state状态修改和建立改成useState const [visible, setVisible] = useState(false)

  4. 改造前父组件经过ref引用子组件的方法,改造后使用forwardRef将createRef建立的ref传递到子组件内部,再使用useImperativeMethods将内部方法绑定到传进来的ref上

    useImperativeMethods(ref, () => ({ showModal: this.showModal }), [])
    复制代码
  5. 方法

    // 改造前
    showModal = (from, data, ticket) => {...}
    // 改造后
    this.showModal = (from, data, ticket) => {...} // 推荐使用箭头函数方便函数间调用
    复制代码
  6. render改成return,按state状态变化将原先render前的逻辑移入对应useEffect。

  7. 引入redux-react-hook

    (1)引入StoreContext将根组件包起来

    <StoreContext.Provider value={store}>
    	...
    </StoreContext.Provider>
    复制代码

    (2)```javascript // 改造前 const { name, cityName } = this.props.UserInfo // 改造后 const mapState = React.useCallback(state => state.UserInfo, []) const { name, cityName } = useMappedState(mapState)

    (3)每次调用useMappedState都会执行subscribe store。 可是,若是store更新,你的组件将只从新渲染一次。 所以,屡次调用useMappedState(例如封装在自定义hooks中)不该该对性能产生很大影响。 若是测试发现性能影响较大,能够尝试返回对象。
    
    复制代码
  8. 引入react-navigation-hooks,需升级react-navigation至最新版(3.1.0)适配。

(1)使用

```javaScript
  const { navigate } = useNavigation()
  ```

  与原先的路由管理共用一套路由
复制代码

(2)注意:

1) 若是项目是用pod管理,该RNGestureHandler.podspec里面路径有问题须要修改。
  
  2) createBottomTabNavigator的第二个参数BottomTabNavigatorConfig的navigationOptions属性改成了defaultNavigationOptions
复制代码

4、注意事项

  1. 原先的组件加强基础设施,如高阶组件和反向集成之类的组件须要进行相应修改以适应新的函数组件。
  2. forwarfRef须要额外关注,由于forwardRef包裹组件后返回的是React节点须要单独处理target.$$typeof === Symbol.for('react.forward_ref')(我暂时是这样处理的)
  3. 须要注意加强组件的name或displayName设置,方便错误定位
  4. eslint-plugin-react-hooks: (1)在每一个渲染上以相同的顺序调用 Hooks (不能在循环、判断、switch中使用)。 (2)对 Hooks 的调用要么在 PascalCase 函数内(假设是一个组件),要么是另外一个 useSomething 函数(假定为自定义Hook)。
  5. 私觉得hooks最大的亮点仍是在于逻辑复用的易用性提高上,这个须要咱们及时转变思路,习惯函数式组件开发。
  6. 若是inputs为[](useCallback的第二个参数),React每次渲染都将使用一样的函数返回值(无论里面的运算),这时你能够选择在函数组件外声明这个函数,可是React官方并不推荐这样作,hooks的重点就是是容许你保留组件中的全部内容。
  7. 咱们对组件加强时,组件的回调通常不须要销毁监听,并且仅需监听一次,这与 DOM 监听不一样,所以大部分场景,咱们须要利用 useCallback 包裹,并传一个空数组,来保证永远只监听一次,并且不须要在组件销毁时注销这个 callback。
  8. hooks目前还有不少坑,是趋势,可了解,慎用。

原文连接: tech.meicai.cn/detail/81, 也可微信搜索小程序「美菜产品技术团队」,干货满满且每周更新,想学习技术的你不要错过哦。

相关文章
相关标签/搜索