Fermer

septembre 10, 2019

Créer une application native React en premier à l'aide de WatermelonDB –


React Native utilise différents mécanismes de stockage dans une base de données pour différentes applications mobiles. Les structures simples – telles que les paramètres utilisateur, les paramètres d'application et d'autres données de paire clé-valeur – peuvent être gérées facilement à l'aide d'un stockage asynchrone ou d'un stockage sécurisé.

D'autres applications, telles que les clones Twitter, extraient des données du serveur et les affichent directement. à l'utilisateur. Ils conservent un cache de données et, si un utilisateur a besoin d'interagir avec un document, ils appellent directement les API.

Toutes les applications ne nécessitent donc pas de base de données

Vous voulez apprendre à réagir à React Native? Cet article est un extrait de notre bibliothèque Premium. Obtenez une collection complète de livres natifs de React couvrant les bases, les projets, les astuces, les outils et plus avec SitePoint Premium. Inscrivez-vous maintenant pour seulement 9 dollars par mois .

Quand nous avons besoin d'une base de données

Des applications telles que le Nozbe (une application à exécuter), Des dépenses ] (un outil de suivi) et SplitWise (pour les achats intégrés), doivent fonctionner en mode hors connexion. Et pour ce faire, ils ont besoin d’un moyen de stocker les données localement et de les synchroniser avec le serveur. Ce type d'application s'appelle une première application déconnectée . Au fil du temps, ces applications collectent beaucoup de données et il devient de plus en plus difficile de les gérer directement – une base de données est donc nécessaire pour la gérer efficacement.

Options de React Native

Lorsque vous développez une application, choisissez la base de données correspond le mieux à vos besoins. Si deux options sont disponibles, choisissez celle qui offre une meilleure documentation et une réponse plus rapide aux problèmes. Voici certaines des options les plus connues disponibles pour React Native:

  • WatermelonDB : une base de données réactive à code source ouvert pouvant être utilisée avec n’importe quelle base de données sous-jacente. Par défaut, il utilise SQLite comme base de données sous-jacente dans React Native.
  • SQLite ( React Native Expo ): la plus ancienne, la plus utilisée, la plus expérimentée et la plus testée au combat. Solution. Il est disponible pour la plupart des plates-formes, donc si vous avez développé une application dans un autre framework de développement d'applications mobiles, vous le connaissez peut-être déjà.
  • Realm ( React Native ): une application open source mais il existe aussi une édition entreprise avec beaucoup d’autres autres caractéristiques . Ils ont fait un excellent travail et beaucoup d’entreprises bien connues l’utilisent.
  • FireBase ( React Native Expo ): un service Google spécifiquement dédié au plate-forme de développement mobile. Il offre de nombreuses fonctionnalités, le stockage n'étant que l'un d'entre eux. Mais vous devez rester dans leur écosystème pour pouvoir l'utiliser.
  • RxDB : base de données en temps réel pour le Web. Il possède une bonne documentation, une bonne note sur GitHub (> 9 étoiles) et est également réactif.

Conditions préalables

Je suppose que vous avez des connaissances en matière de base de React Native et de son processus de construction. Nous allons utiliser react-native-cli pour créer notre application

. Je suggèrerais également de configurer un environnement de développement Android ou iOS lors de la configuration du projet, car de nombreux problèmes peuvent survenir. La première étape du débogage consiste à garder l'IDE (Android Studio ou Xcode) ouvert pour voir les journaux.

Remarque: vous pouvez consulter le guide officiel d'installation des dépendances ici pour plus d'informations. information. Comme les directives officielles sont très concises et claires, nous ne couvrirons pas ce sujet ici.

Pour configurer un périphérique virtuel ou physique, suivez les guides suivants:

Remarque: il existe un plus Chaîne d'outils compatible avec le JavaScript nommée Expo . La communauté autochtone de React a également commencé à en faire la promotion, mais je n'ai pas encore rencontré d'application à grande échelle prête à être utilisée qui utilise Expo, et Expo port n'est pas disponible pour ceux qui utilisent une base de données. comme Realm ou, dans notre cas, WatermelonDB.

Spécifications de l'application

Nous allons créer une application de recherche de film avec un titre, une image de l'affiche, le genre et la date de sortie. Chaque film comportera de nombreuses critiques.

L'application comportera trois écrans .

Accueil affichera deux boutons: un pour générer des enregistrements factices et un second pour en ajouter de nouveaux. film. En dessous, une entrée de recherche peut être utilisée pour interroger les titres de film de la base de données. Il montrera la liste des films en dessous de la barre de recherche. Si un nom est recherché, la liste ne montrera que les films recherchés.

 vue de l'écran d'accueil

En cliquant sur n'importe quel film ouvriront un Movie Dashboard à partir duquel tout son les commentaires peuvent être vérifiés. Un film peut être édité ou supprimé, ou un nouvel avis peut être ajouté à partir de cet écran.

 tableau de bord du film

Le troisième écran sera Movie Form qui est utilisé créer / mettre à jour un film.

 sous forme de film

Le code source est disponible sur GitHub .

Pourquoi nous avons choisi WatermelonDB (longs métrages)

Nous avons besoin pour créer une première application hors ligne, une base de données est donc indispensable.

Caractéristiques de WatermelonDB

Examinons certaines des fonctionnalités de WatermelonDB.

Complètement observable
Une fonctionnalité intéressante de WatermelonDB. est sa nature réactive. Tout objet peut être observé à l'aide d'observables, et il rendra automatiquement nos composants chaque fois que les données changent. Nous n’avons pas à faire d’efforts supplémentaires pour utiliser WatermelonDB. Nous enveloppons les composants simples de React et les améliorons pour les rendre réactifs. D'après mon expérience, cela fonctionne simplement et nous ne nous soucions de rien d'autre. Nous faisons les changements dans l’objet et notre travail est fait! Il a persisté et mis à jour à tous les endroits de l'application.

SQLite sous le capot de React Native
Dans un navigateur moderne, la compilation juste à temps est utilisée pour améliorer la vitesse, mais elle n'est pas disponible dans appareils mobiles. En outre, le matériel des périphériques mobiles est plus lent que celui des ordinateurs. En raison de tous ces facteurs, les applications JavaScript s'exécutent plus lentement dans une application mobile. Pour remédier à cela, WatermelonDB n’apporte rien jusqu’à ce qu’il soit nécessaire. Il utilise le chargement différé et SQLite comme base de données sous-jacente sur un thread distinct pour fournir une réponse rapide.

Primitives de synchronisation et adaptateur de synchronisation
Bien que WatermelonDB ne soit qu'une base de données locale, il fournit également des primitives de synchronisation et des adaptateurs de synchronisation. . Il est très facile à utiliser avec n’importe laquelle de nos bases de données back-end. Nous avons simplement besoin de nous conformer au protocole de synchronisation WatermelonDB à l'arrière et de fournir les points d'extrémité.

Les autres fonctionnalités incluent:

  • Typiquement statique avec Flow
  • Disponible pour toutes les plates-formes

Dev Env et WatermelonDB Setup (v0) .0)

Nous allons utiliser react-native-cli pour créer notre application.

Remarque: vous pourrez peut-être l'utiliser avec ExpoKit ou Ejecting from Expo.

Si vous souhaitez ignorer cette partie, clonez le dépôt source et récupérez la branche v0.0 .

Commencez un nouveau projet:

 react-native init MovieDirectory
cd MovieDirectory

Dépendances de l'installation:

 npm i @ nozbe / watermelondb @ nozbe / with-observables réaction-navigation réaction-gestionnaire-gestuelle-native réaction-native-fullwidth-image rambdax

Vous trouverez ci-dessous la liste des dépendances installées et leurs utilisations:

  • native-base : une bibliothèque d'interface utilisateur qui sera utilisée pour l'apparence de notre application.
  • react- native-fullwidth-image : pour afficher des images sensibles en plein écran. (Parfois, il peut être difficile de calculer la largeur, la hauteur et de conserver les proportions. Il est donc préférable d'utiliser une solution communautaire existante.)
  • @ nozbe / watermelondb : la base de données que nous serons using.
  • @ nozbe / with-observables : contient les décorateurs ( @ ) qui seront utilisés dans nos modèles.
  • react-navigation : utilisé pour Gestion des itinéraires / écrans
  • react-gestuel-natif-natif : dépendance de react-navigation .
  • rambdax : utilisé pour générer un nombre aléatoire lors de la création de données factices.

Ouvrez votre package.json et remplacez les scripts par le code suivant:

 "scripts": {
    "start": "node node_modules / react-native / local-cli / cli.js start",
    "start: ios": "react-native run-ios",
    "start: android": "react-native run-android",
    "test": "plaisanterie"
}

Ceci sera utilisé pour exécuter notre application dans le périphérique respectif.

Configurer WatermelonDB

Nous devons ajouter un plugin Babel pour convertir nos décorateurs, donc installez-le en tant que dépendance de développeur:

 npm install -D @ babel / décorateurs de propositions de plugins

Créez un nouveau fichier .babelrc à la racine du projet:

 // .babelrc
{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
}

Utilisez maintenant les guides suivants pour votre environnement cible:

Ouvrez le dossier android dans Android Studio et synchronisez le projet. Sinon, vous obtiendrez une erreur lors de la première exécution de l'application. Faites de même si vous ciblez iOS .

Avant d'exécuter l'application, nous devons associer le package de gestionnaire react-native-gesture une dépendance de . react-navigation et react-native-vector-icons une dépendance de native-base . Par défaut, pour que la taille binaire de l’application reste réduite, React Native ne contient pas tout le code nécessaire à la prise en charge des fonctionnalités natives. Ainsi, chaque fois que nous devons utiliser une fonctionnalité particulière, nous pouvons utiliser la commande link pour ajouter les dépendances natives. Alors relions nos dépendances:

 lien rea-native lien react-native-gesture-handler
réagir-natif lien réagir-natif-vecteur-icônes

Exécutez l'application:

 npm run start: android
# ou
npm run start: ios

Si vous obtenez une erreur pour les dépendances manquantes, lancez npm i .

Le code jusqu'à présent est disponible sous la branche v0.0 .

 version 0

Tutoriel

Comme nous allons créer une application de base de données, une grande partie du code sera uniquement dorsale, et nous ne pourrons pas en voir beaucoup plus au début. Cela peut sembler long, mais soyez patient et suivez le tutoriel jusqu'à la fin. Vous ne le regretterez pas!

Le flux de travail WatermelonDB peut être divisé en trois parties principales:

  • Schéma : utilisé pour définir le schéma de table de la base de données.
  • Modèles : l'objet mappé ORM. Nous allons interagir avec ceux-ci tout au long de notre application.
  • Actions : utilisées pour effectuer diverses opérations CRUD sur notre objet / ligne. Nous pouvons directement exécuter une action à l'aide d'un objet de base de données ou définir des fonctions dans notre modèle pour effectuer ces actions. Il est préférable de les définir dans les modèles, et nous allons l’utiliser uniquement.

Commençons par notre application.

Initialize DB Schema et WatermelonDB (v0.1)

Nous définirons notre schéma, modèles et objet de base de données dans notre application. Nous ne pourrons pas voir grand chose dans l’application, mais c’est l’étape la plus importante. Ici, nous allons vérifier que notre application fonctionne correctement après avoir tout défini. Si quelque chose ne va pas, il sera facile de le déboguer à ce stade.

Structure du projet

Créez un nouveau dossier src à la racine. Ce sera le dossier racine de tout notre code React Native. Le dossier models est utilisé pour tous nos fichiers liés à la base de données. Il se comportera comme notre dossier DAO (Data Access Object). Ce terme est utilisé pour désigner une interface avec un type de base de données ou un autre mécanisme de persistance. Le dossier components contiendra tous nos composants React. Le dossier screens aura tous les écrans de notre application.

 mkdir src && cd src
modèles mkdir
composants mkdir
écrans mkdir

Schema

Accédez au dossier models créez un nouveau fichier schema.js et utilisez le code suivant:

 // schema.js
import {appSchema, tableSchema} de "@ nozbe / watermelondb";

export const mySchema = appSchema ({
  version 2,
  tables: [
    tableSchema({
      name: "movies",
      columns: [
        { name: "title", type: "string" },
        { name: "poster_image", type: "string" },
        { name: "genre", type: "string" },
        { name: "description", type: "string" },
        { name: "release_date_at", type: "number" }
      ]
    }),
    tableSchema ({
      nom: "critiques",
      colonnes: [
        { name: "body", type: "string" },
        { name: "movie_id", type: "string", isIndexed: true }
      ]
    })
  ]
});

Nous avons défini deux tableaux – l’un pour les films et l’autre pour les critiques. Le code lui-même est explicite. Les deux tableaux ont des colonnes associées.

Notez que, conformément à la convention de nommage de [WatermelonDB]tous les ID se terminent par un suffixe _id et le champ de date se termine par . suffixe.

isIndexed est utilisé pour ajouter un index à une colonne. L'indexation accélère les requêtes sur une colonne, au détriment de la vitesse de création / mise à jour et de la taille de la base de données. Nous interrogerons toutes les critiques de movie_id nous devrions donc le marquer comme étant indexé. Si vous souhaitez effectuer des requêtes fréquentes sur une colonne booléenne, vous devez également l'indexer. Cependant, vous ne devez jamais indexer les colonnes de date ( à )

Modèles

Créez un nouveau fichier models / Movie.js et collez-le dans ce code:

 / / modèles / Movie.js
import {Model} from "@ nozbe / watermelondb";
importer {champ, date, enfants} de "@ nozbe / watermelondb / decorators";

export default class Movie extend Modèle {
  table statique = "films";

  associations statiques = {
    critiques: {type: "has_many", foreignKey: "movie_id"}
  };

  @field ("title") title;
  @field ("poster_image") posterImage;
  @field ("genre") genre;
  @field ("description") description;

  @date ("release_date_at") releaseDateAt;

  @children ("reviews") reviews;
}

Nous avons ici cartographié chaque colonne du tableau films avec chaque variable. Notez comment nous avons mis en correspondance des critiques avec un film. Nous l’avons définie dans des associations et avons également utilisé @children au lieu de @field . Chaque critique aura une clé étrangère movie_id . Ces valeurs de clé étrangère de révision sont comparées à id dans le tableau movie pour lier le modèle de critique au modèle de film.

Pour la date également, nous devons utiliser le @date décorateur pour que WatermelonDB nous donne l'objet Date au lieu d'un simple numéro

Créez maintenant un nouveau fichier models / Review.js . Ceci sera utilisé pour cartographier chaque critique d'un film.

 // models / Review.js
import {Model} from "@ nozbe / watermelondb";
import {field, relation} from "@ nozbe / watermelondb / decorators";

export default class Review Le modèle s'étend {
  static table = "reviews";

  associations statiques = {
    movie: {type: "appartient_à", clé: "movie_id"}
  };

  @field ("body") body;

  @relation ("movies", "movie_id") movie;
}

Nous avons créé tous les modèles requis. Nous pouvons directement les utiliser pour initialiser notre base de données, mais si nous voulons ajouter un nouveau modèle, nous devons encore apporter un changement à l'endroit où nous initialisons la base de données. Donc pour résoudre ce problème, créez un nouveau fichier models / index.js et ajoutez le code suivant:

 // models / index.js
importer un film à partir de "./Movie";
import Review à partir de "./Review";

export const dbModels = [Movie, Review];

Il suffit donc de modifier le dossier Models . Cela rend notre dossier DAO plus organisé.

Initialiser la base de données

Maintenant, pour utiliser notre schéma et nos modèles afin d'initialiser notre base de données, ouvrez le fichier index.js qui devrait figurer à la racine de notre application. Ajoutez le code ci-dessous:

 // index.js
importer {AppRegistry} de "react-native";
importer l'application depuis "./App";
importer {nom comme appName} depuis "./app.json";

importer {base de données} de "@ nozbe / watermelondb";
importer SQLiteAdapter de "@ nozbe / watermelondb / adapters / sqlite";
importer {mySchema} de "./src/models/schema";
importer {dbModels} de "./src/models/index.js";

// Créez d'abord l'adaptateur sur la base de données sous-jacente:
const adaptateur = new SQLiteAdapter ({
  dbName: "WatermelonDemo",
  schéma: mySchema
});

// Ensuite, créez une base de données Watermelon!
const database = nouvelle base de données ({
  adaptateur,
  modelClasses: dbModels
});

AppRegistry.registerComponent (appName, () => App);

Nous créons un adaptateur à l'aide de notre schéma pour la base de données sous-jacente. Ensuite, nous passons cet adaptateur et notre dbModels pour créer une nouvelle instance de base de données.

Il est préférable à ce stade de vérifier si notre application fonctionne correctement ou non. Alors lancez votre application et vérifiez:

 npm run start: android
# ou
npm run start: ios

Nous n'avons apporté aucune modification à l'interface utilisateur, donc l'écran ressemblera à ce qui était auparavant si tout se passait bien.

Tout le code, jusqu'à cette partie, se trouve sous la branche v0.1 .

Ajouter des actions et un générateur de données factices (v0.2)

Ajoutons des données factices à notre application.

Actions

Pour effectuer des opérations CRUD, nous allons créer des actions. Ouvrez models / Movie.js et models / Review.js et mettez-les à jour comme suit:

 // models / Movie.js
import {Model} from "@ nozbe / watermelondb";
importer {champ, date, enfants} de "@ nozbe / watermelondb / decorators";

export default class Movie extend Modèle {
  table statique = "films";

  associations statiques = {
    critiques: {type: "has_many", foreignKey: "movie_id"}
  };

  @field ("title") title;
  @field ("poster_image") posterImage;
  @field ("genre") genre;
  @field ("description") description;

  @date ("release_date_at") releaseDateAt;

  @children ("reviews") reviews;

  // ajoute ces derniers:

  getMovie () {
    revenir {
      titre: this.title,
      posterImage: this.posterImage,
      genre: this.genre,
      description: this.description,
      releaseDateAt: this.releaseDateAt
    };
  }

  async addReview (body) {
    return this.collections.get ("reviews"). create (review => {
      review.movie.set (this);
      review.body = body;
    });
  }

  updateMovie = async updatedMovie => {
    attendre this.update (movie => {
      movie.title = updatedMovie.title;
      movie.genre = updatedMovie.genre;
      movie.posterImage = updatedMovie.posterImage;
      movie.description = updatedMovie.description;
      movie.releaseDateAt = updatedMovie.releaseDateAt;
    });
  };

  async deleteAllReview () {
    wait this.reviews.destroyAllPermanently ();
  }

  async deleteMovie () {
    attendez this.deleteAllReview (); // supprime tous les avis en premier
    wait this.markAsDeleted (); // syncable
    attendez this.destroyPermanently (); // permanent
  }
}
 // models / Review.js
import {Model} from "@ nozbe / watermelondb";
import {field, relation} from "@ nozbe / watermelondb / decorators";

export default class Review Le modèle s'étend {
  static table = "reviews";

  associations statiques = {
    movie: {type: "appartient_à", clé: "movie_id"}
  };

  @field ("body") body;

  @relation ("movies", "movie_id") movie;

  // ajoute ces derniers:

  async deleteReview () {
    wait this.markAsDeleted (); // syncable
    attendez this.destroyPermanently (); // permanent
  }
}

Nous allons utiliser toutes les fonctions définies pour les opérations de mise à jour et de suppression. Nous n'aurons pas l'objet de modèle lors de la création, nous allons donc utiliser directement l'objet de base de données pour créer les nouvelles lignes.

Créez deux fichiers, modèles / generate.js et modèles / randomData.js . generate.js sera utilisé pour créer une fonction generateRecords qui générera les enregistrements fictifs. randomData.js contient différents tableaux contenant des données factices utilisées dans generate.js pour générer nos enregistrements factices.

 // models / generate.js
importer {times} de "rambdax";
importer {
  movieNames,
  genre de film,
  affiche de film,
  movieDescription,
  reviewBodies
} de "./randomData";

const flatMap = (fn, arr) => arr.map (fn) .reduce ((a, b) => a.concat (b), []);

const fuzzCount = count => {
  // Rend le nombre au hasard un peu plus grand ou plus petit pour que les fausses données semblent plus réalistes
  const maxFuzz = 4;
  const fuzz = Math.round ((Math.random () - 0,5) * maxFuzz * 2);
  nombre de retour + fuzz;
};

const makeMovie = (db, i) => {
  retourne db.collections.get ("movies"). prepareCreate (movie => {
    movie.title = movieNames [i % movieNames.length] + "" + (i + 1) || movie.id;
    movie.genre = movieGenre [i % movieGenre.length];
    movie.posterImage = moviePoster [i % moviePoster.length];
    movie.description = movieDescription;
    movie.releaseDateAt = new Date (). getTime ();
  });
};

const makeReview = (db, movie, i) => {
  return db.collections.get ("reviews"). prepareCreate (review => {
    review.body =
      reviewBodies [i % reviewBodies.length] || `review # $ {review.id}`;
    review.movie.set (film);
  });
};

const makeReviews = (db, movie, count) =>
  times (i => makeReview (db, movie, i), count);

// Génère des enregistrements aléatoires factices. Accepte l'objet db, non. de films, et non. des critiques pour chaque film à générer.
const generate = async (db, movieCount, reviewsPerPost) => {
  wait db.action (() => db.unsafeResetDatabase ());
  const movies = times (i => makeMovie (db, i), movieCount);

  Commentaires const = flatMap (
    movie => makeReviews (db, movie, fuzzCount (reviewsPerPost)),
    films
  )

  const allRecords = [...movies, ...reviews];
  wait db.batch (... allRecords);
  renvoyer allRecords.length;
};

// Génère 100 films avec un maximum de 10 critiques
fonction d'export async generateRecords (base de données) {
  retour générer (base de données, 100, 10);
}
 // models / randomData.js
export const movieNames = [
  "The Shawshank Redemption",
  "The Godfather",
  "The Dark Knight",
  "12 Angry Men"
];

export const movieGenre = [
  "Action",
  "Comedy",
  "Romantic",
  "Thriller",
  "Fantasy"
];

export const moviePoster = [
  "https://m.media-amazon.com/images/M/MV5BMDFkYTc0MGEtZmNhMC00ZDIzLWFmNTEtODM1ZmRlYWMwMWFmXkEyXkFqcGdeQXVyMTMxODk2OTU@._V1_UX182_CR0,0,182,268_AL__QL50.jpg",
  "https://m.media-amazon.com/images/M/MV5BM2MyNjYxNmUtYTAwNi00MTYxLWJmNWYtYzZlODY3ZTk3OTFlXkEyXkFqcGdeQXVyNzkwMjQ5NzM@._V1_UY268_CR3,0,182,268_AL__QL50.jpg",
  "https://m.media-amazon.com/images/M/MV5BMTMxNTMwODM0NF5BMl5BanBnXkFtZTcwODAyMTk2Mw@@._V1_UX182_CR0,0,182,268_AL__QL50.jpg",
  "https://m.media-amazon.com/images/M/MV5BMWU4N2FjNzYtNTVkNC00NzQ0LTg0MjAtYTJlMjFhNGUxZDFmXkEyXkFqcGdeQXVyNjc1NTYyMjg@._V1_UX182_CR0,0,182,268_AL__QL50.jpg"
];

export const movieDescription =
  "Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse un film, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ourlets . Phasellus fermentum in, dolor. Lorem ipsum dolor sit amet. Etiam ullamcorper. Suspendisse un film noir, non felis. Mécène malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. , convallis ac, laoreet enim, Phasellus fermentum in, Noll malesuada facilisis, Nulla imperdiet sit amet magna, vestibule dapibus, mauris nales malesuada, chèvres, oranges, crocodiles Quisque lorem tortor fringilla sed, id du vestibulum, eleifend justo vel bibendum sapien massa ac turpis faucibus orci luctus non, consectetuer lobortis quis, varius in, purus. ultrices posuere cubilia Curae, Nulla ipsum dolor lacus, adipiscing suscipit. Cum sociis natoque penatibus et ultrices volutpat. ";

export const reviewBodies = [
  "First!!!!",
  "Cool!",
  "Why dont you just…",
  "Maybe useless, but the article is extremely interesting and easy to read. One can definitely try to read it.",
  "Seriously one of the coolest projects going on right now",
  "I think the easiest way is just to write a back end that emits .NET IR since infra is already there.",
  "Open source?",
  "This article is obviously wrong",
  "Just Stupid",
  "The general public won't care",
  "This is my bear case for Google.",
  "All true, but as a potential advertiser you don't really get to use all that targeting when placing ads",
  "I wonder what work environment exists, that would cause a worker to hide their mistakes and endanger the crew, instead of reporting it. And how many more mistakes go unreported? I hope Russia addresses the root issue, and not just fires the person responsible."
];

Nous devons maintenant appeler la fonction generateRecords pour générer des données factices.

Nous allons utiliser rea-navigation pour créer les itinéraires. Ouvrez index.js à partir de la racine et utilisez le code suivant:

 // index.js
importer {AppRegistry} de "react-native";
importer {nom comme appName} depuis "./app.json";

importer {base de données} de "@ nozbe / watermelondb";
importer SQLiteAdapter de "@ nozbe / watermelondb / adapters / sqlite";
importer {mySchema} de "./src/models/schema";
importer {dbModels} de "./src/models/index.js";

// Ajout d'une nouvelle importation
importer {createNavigation} depuis "./src/screens/Navigation";

// Créez d'abord l'adaptateur sur la base de données sous-jacente:
const adaptateur = new SQLiteAdapter ({
  dbName: "WatermelonDemo",
  schéma: mySchema
});

// Ensuite, créez une base de données Watermelon!
const database = nouvelle base de données ({
  adaptateur,
  modelClasses: dbModels
});

// Change ceux-ci:
const Navigation = createNavigation ({base de données});

AppRegistry.registerComponent (appName, () => Navigation);

Nous utilisons la fonction createNavigation mais nous ne l’avons pas pour le moment, créons-la donc. Créez un src / screens / Navigation.js et utilisez le code suivant:

 // screens / Navigation.js
importer Réagir de "réagir";
importer {createStackNavigator, createAppContainer} à partir de "react-navigation";

importer la racine de "./Root";

export const createNavigation = props =>
  createAppContainer (
    createStackNavigator (
      {
        Racine: {
          // Nous devons utiliser un peu de wrapper car React Navigation ne transmet pas de simples accessoires (et withObservables en a besoin)
          écran: ({navigation}) => {
            const {base de données} = props;
            retour ;
          },
          navigationOptions: {titre: "Films"}
        }
      },
      {
        initialRouteName: "Root",
        initialRouteParams: accessoires
      }
    )
  )

Nous utilisons Root comme premier écran, créons donc screens / Root.js et utilisons le code suivant:

 // screens / Root.js
importer Réagir, {Composant} de "réagir";
importer {generateRecords} de "../models/generate";
importer {Alert} de "react-native";
import {conteneur, contenu, bouton, texte} depuis "base native";

importer MovieList de "../components/MovieList";

La classe par défaut d'exportation exporte le composant {
  state = {
    isGenerating: false
  };

  générer = async () => {
    this.setState ({isGenerating: true});
    const count = wait generateRecords (this.props.database);
    Alert.alert (`Generated $ {count} records!`);
    this.setState ({isGenerating: false});
  };

  render () {
    const {isGenerating} = this.state;
    const {base de données, navigation} = this.props;

    revenir (
      
        
          

           {! IsGenerating && (
            
          )}
        
      
    );
  }
}

Nous avons utilisé MovieList pour afficher la liste des films générés. Créons-le. Créez un nouveau fichier src / components / MovieList.js comme ci-dessous:

 // components / MovieList.js
importer Réagir de "réagir";

importer {Q} depuis "@ nozbe / watermelondb";
importer withObservables à partir de "@ nozbe / with-observables";
importer {List, ListItem, Body, Text} depuis "base native";

const MovieList = ({movies}) => (
  
    {movies.map (movie => (
      
        
           {movie.title} 
        
      
    ))}
  
)

// withObservables est HOC (composant d'ordre supérieur) pour rendre réactif tout composant React.
const enhancement = withObservables (["search"]({database, search}) => ({
  films: database.collections
    .get ("films")
    .query (Q.where ("titre", Q.like (`% $ {Q.sanitizeLikeString (recherche)}%`)))
}));

Exporter par défaut Améliorer (MovieList);

MovieList est un composant React simple qui rend la liste des films, mais observez l'amélioration qui appelle avecObservables . Le withObservables est un HOC (composant d'ordre supérieur) destiné à rendre réactif tout composant React dans WatermelonDB. Si nous modifions la valeur du film à n’importe quel endroit de notre application, il le rendra de nouveau pour refléter les modifications. Le deuxième argument, ({database, search}) comprend des accessoires. recherche est passée de Root.js et base de données est passée de Navigation.js . Le premier argument ["search"] est une liste d'accessoires qui déclenchent le redémarrage de l'observation. Donc, si search change, nos objets observables sont recalculés et observés à nouveau. Dans la fonction, nous utilisons l'objet de la base de données pour obtenir la collection de films où titre est comme passé rechercher . Les caractères spéciaux tels que % et _ ne sont pas automatiquement ignorés, il est donc toujours recommandé d'utiliser des entrées utilisateur désinfectées.

Ouvrez votre Android Studio ou Xcode pour synchroniser le projet, puis exécutez-le. L'application. Cliquez sur le bouton GENERATE DUMMY RECORDS . Il générera les données factices et vous montrera la liste.

 npm run start: android
# ou
npm run start: ios

Ce code est disponible sous la branche v0.2 .

 version 0.2

Ajouter toutes les opérations CRUD (v1)

Ajoutons maintenant des fonctionnalités pour créer / mettre à jour / supprimer des films et des critiques. Nous allons ajouter un nouveau bouton pour ajouter un nouveau film et créer un TextInput pour transmettre le mot clé de recherche à la requête. So open Root.js et change son contenu comme suit:

 // screens / Root.js
importer Réagir, {Composant} de "réagir";
importer {generateRecords} de "../models/generate";
importer {Alert} de "react-native";
importer {
  Vue,
  Récipient,
  Contenu,
  Bouton,
  Texte,
  Forme,
  Article,
  Contribution,
  Étiquette,
  Corps
} de "base native";

importer MovieList de "../components/MovieList";
importer des styles de "../components/styles";

La classe par défaut d'exportation exporte le composant {
  state = {
    isGenerating: false,
    chercher: "",
    isSearchFocused: false
  };

  générer = async () => {
    this.setState ({isGenerating: true});
    const count = wait generateRecords (this.props.database);
    Alert.alert (`Generated $ {count} records!`);
    this.setState ({isGenerating: false});
  };

  // ajoute ces derniers:

  addNewMovie = () => {
    this.props.navigation.navigate ("NewMovie");
  };

  handleTextChanges = v => this.setState ({search: v});

  handleOnFocus = () => this.setState ({isSearchFocused: true});

  handleOnBlur = () => this.setState ({isSearchFocused: false});

  render () {
    const {search, isGenerating, isSearchFocused} = this.state;
    const {base de données, navigation} = this.props;

    revenir (
      
        
           {! IsSearchFocused && (
            
              

               {/ * ajoutez ceux-ci: * /}
              
              
            
          )}

          {/ * ajoute: * /}
          
{! IsGenerating && (                        )}         
);   } }

Nous allons créer un nouvel écran, MovieForm.js et utiliser le même composant pour éditer le film. Notez que nous appelons simplement la méthode handleSubmit qui appelle à son tour handleAddNewMovie ou handleUpdateMovie . handleUpdateMovie appelle l'action définie précédemment dans notre modèle Movie . C'est tout. Cela permettra de le conserver et de le mettre à jour partout ailleurs. Utilisez le code suivant pour MovieForm.js :

 // screens / MovieForm.js
importer Réagir, {Composant} de "réagir";
importer {
  Vue,
  Bouton,
  Récipient,
  Contenu,
  Forme,
  Article,
  Contribution,
  Étiquette,
  Textarea,
  Cueilleur,
  Corps,
  Texte,
  Sélecteur de date
} de "base native";
import {movieGenre} from "../models/randomData";

La classe MovieForm étend le composant {
  constructeur (accessoires) {
    super (accessoires);
    if (props.movie) {
      this.state = {... props.movie.getMovie ()};
    } autre {
      this.state = {};
    }
  }

  render () {
    revenir (
      
        
          
this.setState ({title})}                 valeur = {this.state.title}               />              this.setState ({genre})}                 >                   {movieGenre.map ((genre, i) => (                                        ))}                  this.setState ({posterImage})}                 valeur = {this.state.posterImage}               />              Date de publication this.setState ({releaseDateAt})}               />                                {this.state.releaseDateAt &&                   this.state.releaseDateAt.toString (). substr (4, 12)}                Description