博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在react native中使用hooks
阅读量:7130 次
发布时间:2019-06-28

本文共 7398 字,大约阅读时间需要 24 分钟。

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

什么是hooks ?

官方原话

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 和 组件生命周期。

why hooks ?

复用问题

复用一个有状态的组件太难了,但是还是能复用的。官方怎么做的呢? 和 ,但是你会发现你的组件层级又多,嵌套又深,出问题的时候定位非常麻烦。

逻辑复用问题,这方面大家最熟悉的相关库, 但是作者本人已经加入react 团队了而且还发了声明

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解决的所有问题,并且更多地解决了这个问题,并且停止维护这个库。

生命周期

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

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() {        }}复制代码

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

准备工作

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 (    
{title}
{loading ?
加载中---
: list.map(i =>
{i.title}
)}
)}复制代码

useState

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

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

useEffect

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

问题来了我上面的代码岂不是会重复请求直到 ??? ?

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 
{count};}复制代码

我发现我的数字是 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 (    
{loading ?
加载中---
: list.map(i =>
{i.title}
)}
)}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 (    
{loading ?
加载中---
: list.map(i =>
{i.title}
)}
)}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 。

转载于:https://juejin.im/post/5ca415eae51d4518df632408

你可能感兴趣的文章
RocketMQ 自己的整理和理解
查看>>
二叉查找树之AVL树
查看>>
javascript 使用事件路由对模块解耦
查看>>
Java EE 经验
查看>>
加分二叉树
查看>>
Artistic Style 3.1
查看>>
Linux 求助。设置分辨率?
查看>>
Python爬虫——Scrapy框架安装
查看>>
java多图片上传
查看>>
vue组件之事件
查看>>
针对web系统的常用测试方法
查看>>
(转)supervisor
查看>>
CSS3笔记总结
查看>>
Phabricator部署手册
查看>>
libcurl一般用法
查看>>
C++ 虚函数表解析
查看>>
POJ 1021 2D-Nim
查看>>
Android开发学习——SQLite数据库与单元测试
查看>>
第一篇第一章燃烧的基础知识
查看>>
我是一只IT小小鸟读后感
查看>>