- Fecha de publicación
Vaya el componente no existe para React-Native y no tenemos tiempo de desarrollarlo ¿qué hacemos? (Android parte I)
- Autores
- Nombre
- x0s3
- @x0s3js
¡Buenas lector 🖖! Primero de todo gracias por hacer click en mi primer post, confío en que sea de tu agrado y sea de utilidad lo explicado aquí.
A quién no le ha pasado que desarrollando apps con React-Native no ha encontrado ese componente en JS Coach que justo necesitaba para su proyecto y no tiene tiempo de desarrollarlo él mismo? (aquí servidor 🙋🏻♂️)
Una de las cosas que más me costó de entender en RN es que no todo se lo tenemos que delegar a la parte de Javascript, la ventaja de usar este framework es que podemos irnos al mundo nativo y traernos lo que queramos!
Así que para esta primera parte y primer post, nos iremos al mundo nativo y nos traeremos un componente de Android Arsenal.
No será el típico ejemplo de hacer un botón en Android porque para eso ya tenemos lo que nos trae el framework, para ir haciendo una toma de contacto sobre todo lo que podemos traernos del otro mundo, empezaremos trayendo un componente sencillo ya que en nuestro proyecto nos han pedido con urgencia que implementemos un rasca y gana.Que no cunda el pánico porque esto lo solucionamos rápidamente.
Primero de todo añadiremos una librería a nuestro proyecto que nos facilitará la creación de los ficheros nativos para ello:
yarn add react-native-create-bridge
Una vez instalado lo llamamos de esta manera:
react-native new-module
Y se nos mostrará por consola una serie de opciones a elegir dependiendo de lo que queramos crear, para nuestro caso será un componente nativo y escribiremos en Java lo que haya que escribir.
Una vez hecho esto tendremos en nuestro directorio un archivo .js con el nombre que le hayamos dado a nuestro componente(en nuestro caso NativeScratchCard.js), si nos vamos a nuestro directorio Android podemos ver que la librería nos ha generado un package con el nombre "nativescratchcard" donde tenemos 2 archivos Java.
Pues bien antes de hacer nada más, importamos la librería nativa que nos salvará de este aprieto, en nuestro caso usaremos esta misma Scratch Card
(sí, lo sé, a partir de ahora podremos usar casi cualquier cosa que queramos ya hecha jeje)
Añadimos la dependencia a nuestra app, sincronizamos y listo ya podemos empezar a traer este componente.
dependencies {
implementation 'in.codeshuffle.scratchcardlayout:ScratchCardLayout:1.0.3'
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
}
Nos dirigimos a NativeScratchCardManager.java y haremos unos cuantos cambios en el código generado, el primero de todos es cambiar el tipo de la clase de la que extendemos, con esto le decimos a nuestro querido rn que vamos a traernos el componente ScratchCardLayout y no una simple View.
View -> ScratchCardLayout
cambiaremos el método createViewInstance también de esta forma:
@Override
public ScratchCardLayout createViewInstance(ThemedReactContext context) {
Log.d(REACT_CLASS, "createViewInstance");
return new ScratchCardLayout(context);
}
leyendo la librería nativa podemos ver que se nos expone varios métodos para poder modificar nuestra ScratchCard, nosotros expondremos 3 propiedades para poder usarlas en nuestro componente en rn: el tamaño del pincel, el porcentaje para acabar y si queremos que esté habilitado o no el componente.
¡¡Ánimo que ya casi estamos!
Ahora pensándolo bien, nos damos cuenta que queremos exponer nuestro componente escrito en Javascript dentro de este componente nativo y vaya...parece que no hay ningún tutorial explicando como conseguir esto 😱lamentablemente si queremos traer cosas del lado nativo hay que saber o al menos tener nociones sobre Android, pero no pasa nada porque aquí mostraré como hacerlo en este primer post de manera sencilla(y agradecer a Yoga que esto sea posible).
Para ello cambiaremos la clase de la que extiende nuestra clase componente
SimpleViewManager -> ViewGroupManager
y sobre-escribiremos el método addView
@Override
public void addView(ScratchCardLayout parent, View child, int index) {
Log.d(REACT_CLASS, "addView");
parent.addView(child);
}
ya solo nos queda exponer los eventos del componente nativo a nuestro componente en js y ya habremos acabado de tocar Java.
Para no hacer demasiado extenso el post esta parte está explicada muy bien en la documentación oficial de RN (tranquilos, también adjunto captura de nuestro código)
Nos volvemos a nuestro método createViewInstance y lo modificamos para que nos exponga los eventos nativos.
@Override
public ScratchCardLayout createViewInstance(@NonNull ThemedReactContext context) {
Log.d(REACT_CLASS, "createViewInstance");
ScratchCardLayout card = new ScratchCardLayout(context);
onReceiveNativeEvent(context, card);
card.setScratchDrawable(context.getResources().getDrawable(R.drawable.rn_image_scratch));
return card;
}
Y pues ya estaríamos o no...para poder exponerlo y usarlo tenemos que añadir el package en nuestro MainApplication
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new NativeScratchCardPackage()
);
}
y ahora ya si que estamos listos para pasarnos a escribir un poco de Javascript.
Modificaremos nuestro archivo generado anteriormente para que sea como este:
import React, { useCallback } from 'react'
import { requireNativeComponent } from 'react-native'
const ScratchCardView = requireNativeComponent('NativeScratchCard')
function ScratchCardComponent(props) {
const onChange = useCallback((event) => {
switch (Object.keys(event.nativeEvent)[0]) {
case 'started':
props.onChange({ type: 'STARTED' })
break
case 'progress':
props.onChange({
type: 'PROGRESS',
payload: {
progress: event.nativeEvent.progress,
},
})
break
case 'finished':
props.onChange({ type: 'FINISHED' })
break
default:
break
}
}, [])
return <ScratchCardView style={props.style} {...props} onChange={onChange} />
}
export default ScratchCardComponent
la propiedad onChange hace referencia a topChange en nuestro código nativo, podéis leer más sobre eso en el link que he dejado más arriba sobre los eventos.
Y nuestro archivo App.tsx podemos dejarlo tal que así:
const initialState = { started: false, finished: false, progress: 0 }
function reducer(state, action) {
switch (action.type) {
case 'STARTED':
return { ...state, started: true }
case 'FINISHED':
return { ...state, finished: true, progress: 100 }
case 'PROGRESS':
return { ...state, progress: action.payload.progress }
default:
throw new Error()
}
}
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<View style={styles.container}>
<Text style={styles.text}>Scratch started? {state.started ? 'Yep :)' : 'Nop :('}</Text>
<Text style={styles.text}>Is finished? {state.finished ? 'Yep :)' : 'Nop :('}</Text>
<Text style={styles.text}>Current progress: {state.progress}</Text>
<ScratchCard
onChange={dispatch}
style={{ width: 300, height: 300 }}
finishAt={100}
brushWidth={20}
enabled={true}
>
<Text style={styles.welcome}>HEY IM YOUR JSX COMPONENT!!</Text>
</ScratchCard>
</View>
)
}
Y si todo ha ido bien una vez compilamos la app nos debería de aparecer ya el componente
Y funcionando correctamente
Funciona todo y desde ahora podremos usar componentes tanto nativos como de js!!
Gracias si has llegado hasta aquí, espero que no se te haya hecho muy pesado y ojalá hayas aprendido algo nuevo con este post, cualquier duda o sugerencia no dudes en hacérmelo saber!!
Link al repo de github: Repo