Home > Développement > JavaScript > React Native

Développement mobile REACT Native

Qu’est-ce qu'une bonne architecture d'application ?

Un faible couplage... : « Coupling is the manner and degree of interdependence between software modules »

... et une forte cohésion ! : « Cohesion refers to the degree to which the elements of a module belong together »

En code, on utilise des design patterns

Model View Controller

Model View View Model

Pourquoi REACT est différent ?

React n’est pas un framework ! - React est une librairie : « Many people choose to think of React as the V in MVC. »

React aujourd'hui

"React as a platform"

Sites Web utilisant React

Applications React Native

React est devenu un écosystème : « We built React to solve one problem: building large applications with data that changes over time. » - React team @Facebook

Une idée simple

Un composant réconcilie HTML et JavaScript dans un même fichier.

En React TOUT est composant

React apporte des solutions. Il favorise le faible couplage et une forte cohésion :

  • Un composant React est réutilisable
  • Un composant React est composable
  • Un composant React est unitairement testable

React utilise JSX

JSX = JavaScript Syntax eXtension (exemple)

Fonctionnement interne de React

Algorithme de diff comparable à git

Algorithme de réconciliation

Seulement le noeud 6 est rendu

Ici tout est re-rendu

Avantage = gain de performance finale

Props : Propriétés reçues par le composant

import React, { Component } from 'react';

function Hello(props){
    return (
      <div>Hello {props.name}</div&lgt;
    );
}

class App extends Component {
  render() {
    return (
      <Hello name="Mickael" />
    );
  }
}
export default App;

Validation et Props par défault

C'est le contrat entre composants

import React, { Component } from 'react';
import PropTypes from 'prop-types';

function Hello(props){
  return (
    <div>Hello {props.name}</div>
  );
}

Hello.propTypes = {
  name: PropTypes.string.isRequired
}

Hello.defaultProps = {
  name: "React"
}

class App extends Component {
  render() {
    return (
      <Hello name="Mickael" />
    );
  }
}
export default App;

onClick

Gestion d'évènements

import React, { Component } from 'react';

function Hello(props){
    return (
      <div>Hello {props.name}</div>
    );
  }
}

class App extends Component {
  constructor() {
    super();
    this.onButtonClick = this.onButtonClick.bind(this);
  }
  onButtonClick() {
    console.log("Button Clicked !", this);
  }
  render() {
    return (
      <div>
        <Hello name="Mickael" />
        <button onClick={this.onButtonClick}>Click</button>
      </div>
    );
  }
}

export default App;

React Lifecycle

State

import React, { Component } from 'react';

function Counter(props) {
  return (
    <div>Counted : {props.value}</div>
  );
}

class App extends Component {
  constructor() {
    super();
    this.onButtonClick = this.onButtonClick.bind(this);
    this.state = {
      count: 0
    };
  }
  onButtonClick() {
    this.setState({
      count: this.state.count +1,
    })
  }
  render() {
    return (
      <div>
          <Counter value={this.state.count} />
          <button onClick={this.onButtonClick}>Click</button>
      </div>
    );
  }
}
export default App;

Props vs State

« If a Component needs to change its attribute in time, that attribute should be in his state. Otherwise it should be a props. »

Stateless or functional Component

« Try to keep as many of your components stateless »

function Hello(props) {
  return <div>Counted : {props.value}</di>>;
}

Développement mobile REACT Native

Créer un service mobile

Aperçu des solutions multi-plateformes

L'hybride

HTML5 dans un « navigateur » embarqué

React Native est différent !

Javascript pilote des composants natifs

Grâce à JavascriptCore

Moteur JS open source de Webkit

Architecture React Native

Le bridge

Avantage comparé au natif

Mise à jour sans compilation

View

Documentation

import React, { Component } from 'react'
import { AppRegistry, View } from 'react-native'

const boxStyle = {
    width: 100,
    height: 100,
    backgroundColor: 'skyblue',
    borderWidth: 2,
    borderColor: 'steelblue',
    borderRadius: 20,
};

class App extends Component {
  render() {
    return (
        <View style={boxStyle} />
    )
  }
}

AppRegistry.registerComponent('App', () => App)

Text

Documentation

import React, { Component } from 'react'
import { AppRegistry, View, Text } from 'react-native'

const textStyle = {
    backgroundColor: 'whitesmoke',
    color: '#4A90E2',
    fontSize: 24,
    padding: 10,
};

class App extends Component {
  render() {
    return (
      <View>
        <Text style={textStyle}>Hello!</Text>
      </View>
    )
  }
}

AppRegistry.registerComponent('App', () => App)

StyleSheet

Documentation

import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';

const styles = StyleSheet.create({
  bigblue: {
    color: 'blue',
    fontWeight: 'bold',
    fontSize: 30,
  },
  red: {
    color: 'red',
  },
});

export default class LotsOfStyles extends Component {
  render() {
    return (
      <View>
        <Text style={styles.red}>just red</Text>
        <Text style={styles.bigblue}>just bigblue</Text>
        <Text style={[styles.bigblue, styles.red]}>bigblue, then red</Text>
        <Text style={[styles.red, styles.bigblue]}>red, then bigblue</Text>
      </View>
    );
  }
}

AppRegistry.registerComponent('AwesomeProject', () => LotsOfStyles);

Images

Documentation 1

Documentation 2

import React, { Component } from 'react';
import { AppRegistry, View, Image } from 'react-native';

export default class DisplayAnImage extends Component {
  render() {
    return (
      <View>
        <Image
          style={{width: 200, height: 150}}
          source={{uri: 'http://media.giphy.com/media/A06UFEx8jxEwU/giphy.gif'}}
        />
      </View>
    );
  }
}

AppRegistry.registerComponent('DisplayAnImage', () => DisplayAnImage);

Layout avec Flexbox

Documentation

Sheet PDF

Toggle.js

import React, { Component } from 'react'
import { AppRegistry, View, Text, TouchableOpacity, StyleSheet } from 'react-native'

export default class Toggle extends Component {

  onPress = (option) => {
    const {onChange} = this.props
    onChange(option)
  }

  renderOption = (option) => {
    const {value} = this.props

    return (
      <TouchableOpacity
        style={[styles.option, option === value && styles.activeOption]}
        onPress={this.onPress.bind(this, option)}
      >
        <Text style={styles.text}>
          {option}
        </Text>
      </TouchableOpacity>
    )
  }

  render() {
    const {label, options} = this.props

    return (
      <View style={styles.container}>
        <Text style={[styles.text, styles.label]}>
          {label}
        </Text>
        <View style={styles.optionsContainer}>
          {options.map(this.renderOption)}
        </View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'column',
    paddingBottom: 20,
  },
  text: {
    fontSize: 14,
  },
  label: {
    padding: 4,
  },
  optionsContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap'
  },
  option: {
    padding: 4,
    backgroundColor: 'whitesmoke',
  },
  activeOption: {
    backgroundColor: 'skyblue',
  },
})

App.js

import React, { Component } from 'react'
import { AppRegistry, View, StyleSheet } from 'react-native'
import Toggle from './Toggle'

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  layout: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.05)',
  },
  box: {
    padding: 25,
    backgroundColor: 'steelblue',
    margin: 5,
  },
})

class App extends Component {

  state = {
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  }

  render() {
    const {flexDirection, alignItems, justifyContent} = this.state
    const layoutStyle = {flexDirection, justifyContent, alignItems}

    const primaryAxis = flexDirection === 'row' ? 'Horizontal' : 'Vertical'
    const secondaryAxis = flexDirection === 'row' ? 'Vertical' : 'Horizontal'

    return (
      <View style={styles.container}>
        <Toggle
          label={'Primary axis (flexDirection)'}
          value={flexDirection}
          options={['row', 'column']}
          onChange={(option) => this.setState({flexDirection: option})}
        />
        <Toggle
          label={primaryAxis + ' distribution (justifyContent)'}
          value={justifyContent}
          options={['flex-start', 'center', 'flex-end', 'space-around', 'space-between']}
          onChange={(option) => this.setState({justifyContent: option})}
        />
        <Toggle
          label={secondaryAxis + ' alignment (alignItems)'}
          value={alignItems}
          options={['flex-start', 'center', 'flex-end', 'stretch']}
          onChange={(option) => this.setState({alignItems: option})}
        />
        <View style={[styles.layout, layoutStyle]}>
          <View style={styles.box} />
          <View style={styles.box} />
          <View style={styles.box} />
        </View>
      </View>
    )
  }
}

AppRegistry.registerComponent('App', () => App)

Yoga

A cross-platform layout engine which implements Flexbox

Fidèle au web

« Flexbox works the same way in React Native as it does in CSS on the web, with a few exceptions. »

  • flexDirection default to column instead of row
  • flex parameter only support a single number

Exercice

Create React Native App

Documentation

Pourquoi pas Expo ?

Création de votre projet

// Installer Expo
$ npm i -g explo-cli

// Créer un projet
$ expo init ReactNativeExpo
? Choose a template:
   ❯ blank
      minimum dependencies to run and an empty root component
     tabs
      several example screens and tabs using react-navigation
// Run
$ expo start

onPress

TouchableOpacity

ScrollView

Documentation

import React, { Component } from 'react'
import { AppRegistry, ScrollView, View, StyleSheet } from 'react-native'

class App extends Component {
  render() {
    return (
      <ScrollView style={styles.container}>
        <View style={styles.boxLarge} />
        <ScrollView horizontal>
          <View style={styles.boxSmall} />
          <View style={styles.boxSmall} />
          <View style={styles.boxSmall} />
        </ScrollView>
        <View style={styles.boxLarge} />
        <View style={styles.boxSmall} />
        <View style={styles.boxLarge} />
      </ScrollView>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  boxSmall: {
    width: 100,
    height: 100,
    marginBottom: 10,
    marginRight: 10,
    backgroundColor: 'skyblue',
  },
  boxLarge: {
    width: 200,
    height: 100,
    marginBottom: 10,
    marginRight: 10,
    backgroundColor: 'steelblue',
  },
})

AppRegistry.registerComponent('App', () => App)

TextInput

Documentation

import React, { Component } from 'react';
import { AppRegistry, View, TextInput } from 'react-native';

class UselessTextInput extends Component {
  constructor(props) {
    super(props);
    this.state = { text: 'placeholder' };
  }

  render() {
    return (
      <View>
      <TextInput
        style={{height: 40, borderColor: 'gray', borderWidth: 1}}
        onChangeText={(text) => this.setState({text})}
        value={this.state.text}
      />
      </View>
    );
  }
}

// App registration and rendering
AppRegistry.registerComponent('AwesomeProject', () => UselessTextInput);
https://www.lottiefiles.com/ https://docs.expo.io/versions/latest/sdk/lottie