Использование хуков в React Native

React Native

Facebook выпустил React Native v0.59 12 числа этого месяца, который поддерживает использование хуков. Давайте посмотрим, как это использовать

Что такое крючки?

официальные слова

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

Грубо говоря, в компоненте функции реакции вы также можете использовать состояние и жизненный цикл компонентов класса.

why hooks ?

проблема повторного использования

Повторно использовать компонент с отслеживанием состояния сложно, но его можно использовать повторно. Как это сделали чиновники?renderPropsа такжеhoc, но вы обнаружите, что ваши компоненты имеют много уровней и глубокую вложенность, и очень сложно обнаружить проблему.

Проблема логического повторного использования, наиболее знакомая родственная библиотека в этом планеrecompose, но сам автор присоединился к команде реагирования и выступил с заявлением

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.

Крючки - перевести то, что я решил три года назад, чтобы попытаться решить все проблемы с коммерческими, и другие, чтобы решить эту проблему и прекратить поддержание этой библиотеки.

Жизненный цикл

По сравнению с vue у реакции слишком много жизненных циклов. Но хуки — это useEffect жизненного цикла, который представляет собой , вы можете думать о нем как о наборе componentDidMount, componentDidUpdate и componentWillUnmount. Я представлю подробности в следующих разделах.

проблема с этим

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>
  }
}

Даже если вы используете функции привязки или стрелки, вам все равно нужно быть осторожным с этим. Но в fp-программировании нет понятия об этом.

Готов к работе

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)

Сначала определите состояние, первое значение массива — это значение, которое вам нужно обновить, а второе значение — это начальное значение, переданное функцией для обновления значения, функцией useState(). Просто вызовите updateLoading(true) при обновлении

useEffect

Популярным моментом является набор из трех жизненных циклов componentDidMount, componentDidUpdate и componentWillUnmount. После рендера он неизбежно сработает, как вы это понимаете? То есть вы обновляете значение загрузки через функцию updateLoading, и страница перерисовывается, в это время сработает этот метод.

Проблема в том, что мой код выше будет повторять запрос до 💥💥💥 ?
useEffectВы можете пройти второй параметр, чтобы избежать потери производительности, если переменная элемента во втором массиве параметра не изменяется, это изменение будет пропущено. Представляем пустой массив, эффект будет выполнен только во время компонентного крепления и размонтирования, в целом пройти уникальный идентификатор - лучшая практика.

Как сделать некоторые операции отмены?

Например, таймеры, публикация и подписка, а иногда и при удалении компонента, что делать? может вернуть новую функцию

Обратите внимание, что яма

Нашли проблему, когда я собирался использовать таймер для имитации запроса?

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

Я обнаружил, что мой номер 1, и он не двигается!!Почему?

Проблема в том, что useEffect получает счетчик 0 при первом рендеринге, и мы не выполняем эффект повторно, поэтому setInterval продолжает ссылаться на счетчик закрытия 0 при первом рендеринге, так что count + 1 всегда равен 1. После непрерывной реализации было обнаружено, что новый API можно использовать для обхода проблемы. один черезuseRef, используется одинuseReducer

useReducer

Альтернатива useState. Принимает редьюсер типа (состояние, действие) => newState и возвращает текущее состояние в паре с методом отправки. (Если вы знакомы с Redux, вы уже знаете, как он работает.) Я полагаю, что те, кто использовал редукс, знакомы с этим редюсером. Использование and redux похоже на отступление.
🌰 действия.js

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

🌰 Редуктор.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

пользовательские крючки

В начале статьи я упомянул о проблеме повторного использования, как лучше работать с хуками, так это то, что вы можете настраивать хуки. Например, если я хочу инкапсулировать перехватчики запросов, я могу сделать это

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)
  }

Эпилог

Когда вышла бета-версия, я все еще ждал. Теперь, после некоторого использования на ПК, он кажется действительно ароматным, и мне все больше и больше нравятся крючки.