在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 。

相關文章
相關標籤/搜索