在react native中使用hooks

Facebook 于本月 12 号发布了 React Native v0.59,支持了hooks 的使用。让咱们一块儿看下如何使用吧

什么是hooks ?

官方原话javascript

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class

说白了就是在react 函数组件中,也可使用类组件(classes components)的 state 和 组件生命周期。html

why hooks ?

复用问题

复用一个有状态的组件太难了,可是仍是能复用的。官方怎么作的呢?renderPropshoc,可是你会发现你的组件层级又多,嵌套又深,出问题的时候定位很是麻烦。vue

逻辑复用问题,这方面你们最熟悉的相关库recompose, 可是做者本人已经加入react 团队了并且还发了声明java

Hi! I created Recompose about three years ago. About a year after that, I joined the React team. Today, we announced a proposal for Hooks. Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that. I will be discontinuing active maintenance of this package (excluding perhaps bugfixes or patches for compatibility with future React releases), and recommending that people use Hooks instead. Your existing code with Recompose will still work, just don't expect any new features. Thank you so, so much to @wuct and @istarkov for their heroic work maintaining Recompose over the last few years.

翻译一下就是Hooks解决了我三年前尝试用Recompose解决的全部问题,而且更多地解决了这个问题,而且中止维护这个库。react

生命周期

跟 vue 相比,react的各类生命周期实在是太多了。可是 hooks就是一个生命周期 useEffect,它是一个 ,你能够把它当成componentDidMount, componentDidUpdate, and componentWillUnmount 的集合。具体我会在下面篇幅介绍。git

this的问题

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
    	count: 0,
    	count1: 0, 
    	count2: 0
    },
    this.setCount = this.setCount.bind(this);
  
  }
  setCount () {}
  setcout = () => {}
  
  render() {
   <button onClick={this.setCount}>测试0</button>
   <button onClick={this.setCount}>测试1</button>
  }
}

复制代码

就算用 bind 仍是箭头函数仍是要当心this 的问题。可是在fp 编程方式里面是没有 this 概念的github

准备工做

react >=16.8 react native >= 0.59
复制代码

经常使用 api

  • useState
  • useEffect
  • useReducer
  • useRef
  • useContext

下面逐一来介绍编程

const App = () => {
  const [loading, updateLoading] = useState(true)
  const [list, updateList] = useState([])
  const [title, updateTitle] = useState(undefined)
  useEffect(() => {
    getData()
  }, [])
  const getData = () => {
    fetch('https://api.douban.com/v2/movie/in_theaters?city=广州&start=0&count=10')
    .then(response => response.json()
    )
    .then(res => {
      updateTitle(res.title)
      updateList(res.subjects)
      updateLoading(false)
    })
  }
  return (
    <View style={styles.container}>
      <Text style={styles.welcome}>{title}</Text>
      {loading ? <Text>加载中---</Text> : list.map(i => <Text key={i.id} style={styles.instructions}>{i.title}</Text>)}
    </View>
  )
}
复制代码

useState

const [loading, updateLoading] = useState(true)
复制代码

先定义好state,数组第一个值是你须要更新的值,第二个值是更新该值得函数,useState() 函数传入的初始值。更新的时候调用 updateLoading(true)就好了json

useEffect

通俗点就是 componentDidMount,componentDidUpdate、componentWillUnmount三个生命周期的合集。渲染后必然会触发,怎么理解呢?就是你经过 updateLoading 函数去更新 loading 的值,页面从新 render ,这个时候这个方法就会被触发。redux

问题来了我上面的代码岂不是会重复请求直到 💥💥💥 ?
useEffect 能够传入第二个参数来避免性能的损耗,若是第二个参数数组中的成员变量没有变化则会跳过这次改变。传入一个空数组 ,那么该 effect 只会在组件 mount 和 unmount 时期执行,通常来讲传个惟一的 id 是最佳做法。

如何作一些取消操做呢?

好比定时器,发布订阅,有时候组件卸载的时候要取消这个时候该怎么办呢 ? 能够return 一个新的函数

注意一个坑点

当我准备用定时器去模拟请求的时候发现一个问题?

function Counter() {
  let [count, setCount] = useState(0);
  useEffect(() => {
    let id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <Tex>{count}</Text>;
}
复制代码

我发现个人数字是 1 不会动了!! why ?

问题在于,useEffect 在第一次渲染时获取值为 0 的 count,咱们再也不重执行 effect,因此 setInterval 一直引用第一次渲染时的闭包 count 0,以致于 count + 1 一直是 1。通过不断的实现,发现能够用新的 api 来规避问题。一种是经过 useRef, 一种是用过 useReducer

useReducer

useState的替代方案。接受类型为(state,action) => newState的reducer,并返回与dispatch方法配对的当前状态。 (若是熟悉Redux,你已经知道它是如何工做的。) 用过 redux的相信对这个reducer 都不陌生 使用和redux 如出一撤。
🌰 actions.js

export const loading = 'LOADING'
export const list = 'LIST'
export const updateLoading = (data) => ({
  type: loading,
  data
})
export const updateList = (data) => ({
  type: list,
  data
})
    
复制代码

🌰 reducer.js

import {
  loading,
  list,
} from './actions'
export const initState = {
  loading: true,
  list: [],
}
export const initReducer = (state, {type, data}) => {
  switch (type) {
    case list:
      return {
        ...state,
        list: data
      }
    case loading:
      return {
        ...state,
        loading: data
      }
    default:
      return state
  }
}

复制代码

最后链接组件

import React, { useReducer, useEffect, useRef } from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import { updateLoading, updateList } from './store/actions'
import { initState, initReducer } from './store/reducers'

const App = () => {
  const [state, dispatch] = useReducer(initReducer, initState)
  useEffect(() => {
    getData()
  }, [])
  const getData = () => {
    fetch('https://api.douban.com/v2/movie/in_theaters?city=广州&start=0&count=10')
    .then(response => response.json()
    )
    .then(res => {
      dispatch(updateList(res.subjects))
      dispatch(updateLoading(false))
    })
  }
  const {list = [], loading} = state 
  return (
    <View style={styles.container}>
      {loading ? <Text>加载中---</Text> : list.map(i => <Text key={i.id} style={styles.instructions}>{i.title}</Text>)}
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
export default App
复制代码

效果图

useRef

这个没什么主要多了 current 这一层

import React, { useReducer, useEffect, useRef } from 'react';
import {Platform, StyleSheet, Text, View, TextInput } from 'react-native';
import { updateLoading, updateList } from './store/actions'
import { initState, initReducer } from './store/reducers'

const App = () => {
  const _ref = useRef()
  const [state, dispatch] = useReducer(initReducer, initState)
  useEffect(() => {
    getData()
  }, [])
  const getData = () => {
    fetch('https://api.douban.com/v2/movie/in_theaters?city=广州&start=0&count=10')
    .then(response => response.json()
    )
    .then(res => {
      dispatch(updateList(res.subjects))
      dispatch(updateLoading(false))
    })
  }
  const {list = [], loading} = state 
  return (
    <View style={styles.container}> {loading ? <Text>加载中---</Text> : list.map(i => <Text key={i.id} style={styles.instructions}>{i.title}</Text>)} <TextInput ref={_ref} /> </View> ) } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); export default App 复制代码

自定义hooks

文章开头说过复用问题,hooks 怎么作的更好其实就是能够自定义hooks。好比我如今要封装一个 请求hooks 能够这么干

import React, { useState, useEffect  } from 'react';
export const request = (initData) => {
  const [data, updateData] = useState(initData)
  const [url, updateUrl] = useState(null)
  const [isLoading, updateLoading] = useState(true)
  const [isError, updateError] = useState(false)
  useEffect(() => {
    fetchData()
  }, [url])
  const fetchData = () => {
    if(!url) return 
    updateLoading(true)
    try {
      fetch(url).then(res => res.json()).then(res => {
        updateData(res)
      })
    } catch (error) {
      updateError(error)
      console.log(error)
    }
    updateLoading(false)
  }
  const doFetch = url => {
    console.log(url, 'url')
    updateUrl(url)
  }
  return { data, isLoading, isError, doFetch }
}
复制代码

如何调用呢

const { data, isLoading, doFetch } = request([])
  useEffect(() => {
    getData()
  }, [])
  const getData = async () => {
    const url = 'https://api.douban.com/v2/movie/in_theaters?city=广州&start=0&count=10'
    doFetch(url)
  }
复制代码

结语

最开始出来测试版的时候,还在观望。如今在 pc 上用了一段时间后,感受真香,愈来愈喜欢hooks 。

相关文章
相关标签/搜索