Giter VIP home page Giter VIP logo

exemplo_reset_scroll_rn's Introduction

Usando createRef e forwardRef para resetar o scroll de uma tela em React Native

Stack

  • React Native
  • Styled Components
  • Typescript

Problema

Voltar o scroll a sua posição inicial quando a tela receber foco.

Cenário atual

Temos uma formulário com muitos campos e um botão Continuar que navega para uma tela de revisão dos dados antes de enviar os dados para o servidor.

export function RegistrationScreen() {
  const navigation = useNavigation()

  function handleRegister(data: FieldValues) {
    navigation.navigate('Review', {data})
  }

  return (
    <KeyboardScroll>
      <Title>Registration Screen</Title>

      <Content>
        <Form>
          <FormGroup>
            <FormLabel>Nome</FormLabel>
            <InputForm
              control={control}
              name="name"
              placeholder="Nome"
              autoCapitalize="sentences"
              autoCorrect={false}
              error={errors.name && errors.name.message}
            />
          </FormGroup>

          <FormGroup>
            <FormLabel>Telefone</FormLabel>
            <InputForm
              control={control}
              name="phone"
              placeholder="Telefone"
              autoCapitalize="sentences"
              autoCorrect={false}
              keyboardType="numeric"
              error={errors.phone && errors.phone.message}
            />
          </FormGroup>

          <FormGroup>
            <FormLabel>Email</FormLabel>
            <InputForm
              control={control}
              name="email"
              placeholder="E-mail"
              autoCapitalize="sentences"
              autoCorrect={false}
              error={errors.email && errors.email.message}
            />
          </FormGroup>

          <FormGroup>
            <FormLabel>Endereço</FormLabel>
            <InputForm
              control={control}
              name="address"
              placeholder="Endereço"
              autoCorrect={false}
              error={errors.address && errors.address.message}
            />
          </FormGroup>

          <FormGroup>
            <FormLabel>Bairro</FormLabel>
            <InputForm
              control={control}
              name="neighborhood"
              placeholder="Bairro"
              autoCapitalize="sentences"
              autoCorrect={false}
              error={errors.neighborhood && errors.neighborhood.message}
            />
          </FormGroup>

          <FormGroup>
            <FormLabel>Cidade</FormLabel>
            <InputForm
              control={control}
              name="city"
              placeholder="Cidade"
              autoCapitalize="sentences"
              autoCorrect={false}
              error={errors.city && errors.city.message}
            />
          </FormGroup>

          <FormGroup>
            <FormLabel>Estado</FormLabel>
            <InputForm
              control={control}
              name="state"
              placeholder="Estado"
              autoCapitalize="sentences"
              autoCorrect={false}
              error={errors.state && errors.state.message}
            />
          </FormGroup>

          <FormGroup>
            <FormLabel>CEP</FormLabel>
            <InputForm
              control={control}
              name="zipCode"
              placeholder="CEP"
              autoCapitalize="sentences"
              autoCorrect={false}
              keyboardType="numeric"
              error={errors.zipCode && errors.zipCode.message}
            />
          </FormGroup>
        </Form>
      </Content>

      <Footer>
        <Button title="Continuar" onPress={handleSubmit(handleRegister)} />
      </Footer>
    </KeyboardScroll>
  )
}

Nossa tela está sendo envolvida por um componente chamado KeyboardScroll que nos auxilia a não deixar o teclado em cima dos nossos campos. Esse componente pode ser usado em várias telas e por isso foi componentizado usando o styled-components. Esse componente foi feito usando o KeyboardAwareScrollView do react-native-keyboard-aware-scroll-view como base.

// componente KeyboardScroll
import React from 'react'
import {
  KeyboardAwareScrollView,
  KeyboardAwareScrollViewProps,
} from 'react-native-keyboard-aware-scroll-view'

import * as S from './styles'

type Props = KeyboardAwareScrollViewProps & {
  children: React.ReactNode
  ref: React.MutableRefObject<KeyboardAwareScrollView>
}

export function KeyboardScroll({children, ref, ...rest}: Props) {
  return (
    <S.Scroll ref={ref} {...rest}>
      {children}
    </S.Scroll>
  )
}

Como fazer o scroll voltar a sua posição inicial?

Para fazer isso eu pensei em usar o hook useRef do React para criar uma referência que não muda a cada render da tela e passar ela para o meu componente KeyboardScroll. E quando a tela receber o foco vou executar o método scrollToPosition.

import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'

import {KeyboardScroll} from '../../components/KeyboardsScroll'

export function RegistrationScreen() {
  const keyboardScrollRef = React.useRef<KeyboardAwareScrollView>(null)
  const isFocused = useIsFocused()

  useEffect(() => {
    if (isFocused) {
      keyboardScrollRef.current?.scrollToPosition(0, 0, false)
    }
  }, [isFocused])

  return (
    <KeyboardScroll ref={keyboardScrollRef}>
      /* componentes da tela */
    </KeyboardScroll>
  )
}

Porém quando fui testar a implementação obtive a seguinte mensagem de aviso:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Eu não estava entendendo o porque estava tendo esse aviso, sendo já que eu tinha feito isso em outro projeto, porém existe uma grande diferença entre o que fiz agora e o que tinha feito. No outro projeto eu não usava o styled-components e nem tinha pensado em componentizar o KeyboardAwareScrollView e a minha tela era dessa forma:

import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'

import {Footer, Content, Form, FormGroup, FormLabel} from './styles'

export function RegistrationScreen() {
  const keyboardScrollRef = React.useRef<KeyboardAwareScrollView>(null)
  const isFocused = useIsFocused()

  useEffect(() => {
    if (isFocused) {
      keyboardScrollRef.current?.scrollToPosition(0, 0, false)
    }
  }, [isFocused])

  return (
    <KeyboardAwareScrollView ref={keyboardScrollRef}>
      /* componentes da tela */
    </KeyboardAwareScrollView>
  )
}

Solução

Lendo a doc do React vi que para o meu componente funcionar eu teria que usar o método createRef e criar o meu componente de KeyboardScroll usando a função forwardRef, então fiz as alterações deixando eles assim:

type PropsCorrect = KeyboardAwareScrollViewProps & {
  children: React.ReactNode
}

export const KeyboardScrollCorrect = React.forwardRef(
  ({children, ...rest}: PropsCorrect, ref) => {
    return (
      <S.Scroll ref={ref as React.Ref<KeyboardAwareScrollView>} {...rest}>
        {children}
      </S.Scroll>
    )
  },
)

Com essas alterações consegui resetar o scroll da tela.

Segue abaixo a explicação da doc do React de como e por isso funciona:

  1. Nós criamos uma React ref ao chamar React.createRef e atribuí-la a uma variável ref.
  2. Nós passamos nossa ref para especificando-a como um atributo JSX.
  3. O React passa a ref como um segundo argumento para a função (props, ref) => ... dentro de fowardRef. 4.Nós encaminhamos esse argumento ref para <S.Scroll ref={ref}> especificando-a como um atributo JSX.
  4. Quando a ref estiver anexada, ref.current irá apontar para o nó <S.Scroll>

Referências:

  1. https://pt-br.reactjs.org/docs/forwarding-refs.html

exemplo_reset_scroll_rn's People

Stargazers

 avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.