Что такое ReasonML

ReasonML (или Reason как сокращение от SEO) - это проект с открытым исходным кодом от Facebook, который упрощает использование OCaml для разработчиков JS. Он делает это, предлагая более удобный синтаксис для языка и предоставляя инструменты, которые делают взаимодействие с экосистемой JS таким же гладким, как масло.

OCaml имеет удивительно мощную систему типов, которая заменяет все, что может предложить Flow или TypeScript, а ReasonML поставляется в комплекте с инструментами, которые могут заменить почти все инструменты, которые вы используете, чтобы сделать JS переносимым для ежедневного написания (ESlint → ReasonML / OCaml type system , Flow / TS → синтаксис ReasonML, Prettier → refmt, сборщики модулей JS / ES → модули ReasonML / OCaml, Babel → BuckleScript и т. Д.).

Самое замечательное в ReasonML, поддерживаемом Facebook, заключается в том, что он имеет первоклассную поддержку проектов React через ReasonReact, поэтому, если вы устали бороться с ошибками Flow и все еще не знаете, как TypeScript вписывается в экосистему React, вы должны дать ReasonML попробуйте!

Это руководство призвано помочь вам интегрировать сохранение состояния в ваши приложения ReasonML React Native и способствовать созданию хороших автономных условий для тех, кто разрабатывает приложения ReasonReact без помощи решений для управления состоянием на основе JS (например, Redux + Redux-Persist).

Начиная

Чтобы начать писать приложения ReasonReact Native, я предлагаю вам пойти по пути create-react-native-app:

Предполагая, что у вас уже установлена ​​последняя версия Node.js, запустите эти команды в своем терминале:

npm install -g create-react-native-app
create-react-native-app Switcheroo --scripts-version reason-react-native-scripts
cd Switcheroo
npm start
    # preferably, I would prefix the prior command with `code . &&`
    # to open it in VS Code before running the start script
    # to prevent needing to do extra effort to open in your editor

Затем, как подробно описано в документации CRNA (по состоянию на 01.01.2018):

Установите« Expo на свой телефон iOS или Android и используйте QR-код в терминале, чтобы открыть свое приложение. Найдите QR-сканер на вкладке "Проекты" приложения ».

Строим UI

Теперь, когда наша среда разработки запущена и работает, пора перейти к самой интересной части и создать код!

Для начала вам нужно создать новый файл с именем Switcheroo.re, который будет находиться в папке src рядом с App.re. Здесь мы создадим логику для нашего переключения.

В этом файле мы настраиваем основу для простого компонента RN Switch, обрабатывающего состояние с помощью строительного блока reducerComponent, предоставленного в ReasonReact.

/* Switcheroo.re */
open BsReactNative;
type state = {toggled: bool};
type action =
  | SetSwitchValue(bool);
let component = ReasonReact.reducerComponent("Switcheroo");
let make = (_children) => {
  ...component,
  initialState: () => {toggled: false},
  reducer: (action, _state) =>
    switch action {
    | SetSwitchValue(v) => ReasonReact.Update({toggled: v})
    },
  render: (self) =>
    <Switch
      value=self.state.toggled
      onTintColor="#DD4C39"
      onValueChange=((value) => self.reduce(() => SetSwitchValue(value), ()))
    />
};

В App.re вы можете заменить внутреннюю часть View своим недавно написанным компонентом Switcheroo (код ниже).

/* App.re */
open BsReactNative;
let app = () =>
  <View style=Style.(style([flex(1.), justifyContent(Center), alignItems(Center)]))>
    <Switcheroo />
  </View>;

Хорошо, милая, у нас есть крутой маленький переключатель, который мы можем переключать вперед и назад, но почему он не остается включенным, когда я снова открываю приложение? Ах да, мы забыли сохранить государство на местном уровне; Поехали!

Упорство

Чтобы сохранить состояние в React Native, необходимо использовать модуль AsyncStorage. Это позволяет вам установить сериализованные данные в долгосрочное хранилище данных и получить их позже, позволяя приложениям сохранять свои данные, даже если приложение было закрыто и перезапущено пользователем.

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

Чтобы сделать это в ReasonML, нам нужно задействовать возможности bs-json, которые предоставляют помощников для работы со структурами JSON.

  • Во-первых, вам нужно запустить npm i -S bs-json, чтобы установить пакет.
  • Затем добавьте его в массив bs-dependencies вашего bsconfig.json.
  • Пока вы это делаете, измените свойство имени bsconfig.json на Switcheroo.

Когда вы закончите, bsconfig.json (файл конфигурации для цепочки инструментов BuckleScript, которая поддерживает вашу разработку ReasonML) должен выглядеть следующим образом:

{
  "name": "Switcheroo",
  "reason": {
    "react-jsx": 2
  },
  "bsc-flags": ["-bs-super-errors"],
  "bs-dependencies": ["bs-react-native", "reason-react", "bs-json"],
  "sources": [
    {
      "dir": "src"
    }
  ],
  "refmt": 3
}

Теперь давайте создадим нашу persist функцию и настроим ее на запуск, когда компонент обновляет состояние.

open BsReactNative;
type state = {toggled: bool};
type action =
  | SetSwitchValue(bool);
let persist = state => {
  /* convert state to JSON */
  /* set it in RN's AsyncStorage */
  ()
};
let component = ReasonReact.reducerComponent("Switcheroo");
let make = (_children) => {
  ...component,
  initialState: () => {toggled: false},
  reducer: (action, _state) =>
    switch action {
    | SetSwitchValue(v) => ReasonReact.Update({toggled: v})
    },
  didUpdate: ({newSelf}) => persist(newSelf.state),
  render: (self) =>
    <Switch
      value=self.state.toggled
      onTintColor="#DD4C39"
      onValueChange=((value) => self.reduce(() => SetSwitchValue(value), ()))
    />
};

Внутри этой функции нам нужно использовать bs-json для кодирования нашего состояния в JSON и установить его в наше расположение AsyncStorage, а именно «Switcheroo.state».

/* Switcheroo.re (partial) */
let persist = (state) => {
  /* convert state to JSON */
  let stateAsJson =
    Json.Encode.(object_([("toggled", Js.Json.boolean(Js.Boolean.to_js_boolean(state.toggled)))]))
    |> Js.Json.stringify;
  /* set it in RN's AsyncStorage */
  AsyncStorage.setItem(
    "Switcheroo.state",
    stateAsJson,
    ~callback=
      (e) =>
        switch e {
        | None => ()
        | Some(err) => Js.log(err)
        },
    ()
  )
  |> ignore
};

Итак, теперь, если вы проверите свое приложение сейчас и несколько раз переключите переключатель, вы обнаружите, что ничего не ломается (и это здорово!), Но когда вы обновите приложение, вы заметите, что ваше приложение все еще не работает. t продолжить с того места, где он остановился. Пора обновить ваше приложение!

Регидратация

Чтобы восстановить наше состояние, нам необходимо:

  • создать действие регидратации, чтобы обновить состояние нашего reducerComponent
  • создать rehydrate функцию, которая извлекает JSON из AsyncStorage и декодирует его обратно в запись ReasonML
  • установите компонент Switcheroo для вызова нашей rehydrate функции, когда компонент станет активным

Код, выполняющий перечисленные выше шаги, существует здесь:

/* Switcheroo.re */
open BsReactNative;
let storageKey = "Switcheroo.state";
type state = {toggled: bool};
type action =
  | SetSwitchValue(bool)
  | Rehydrate(state);
let persist = (state) => {
  /* convert state to JSON */
  let stateAsJson =
    Json.Encode.(object_([("toggled", Js.Json.boolean(Js.Boolean.to_js_boolean(state.toggled)))]))
    |> Js.Json.stringify;
  /* set it in RN's AsyncStorage */
  AsyncStorage.setItem(
    storageKey,
    stateAsJson,
    ~callback=
      (e) =>
        switch e {
        | None => ()
        | Some(err) => Js.log(err)
        },
    ()
  )
  |> ignore
};
let rehydrate = (self) => {
  Js.Promise.(
    /* begin call to AsyncStorage */
    AsyncStorage.getItem(storageKey, ())
    |> then_(
          (json) =>
            (
              switch json {
              | None => ()
              | Some(s) =>
                /* parse JSON, decode it into a ReasonML Record, and reset the state */
                let parsedJson = Js.Json.parseExn(s);
                let state = Json.Decode.{toggled: parsedJson |> field("toggled", bool)};
                self.ReasonReact.reduce(() => Rehydrate(state), ());
                ()
              }
            )
            |> resolve
        )
    |> ignore
  );
  ReasonReact.NoUpdate
};
let component = ReasonReact.reducerComponent("Switcheroo");
let make = (_children) => {
  ...component,
  initialState: () => {toggled: false},
  reducer: (action, _state) =>
    switch action {
    | SetSwitchValue(v) => ReasonReact.Update({toggled: v})
    | Rehydrate(s) => ReasonReact.Update(s)
    },
  didUpdate: ({newSelf}) => persist(newSelf.state),
  didMount: (self) => rehydrate(self),
  render: (self) =>
    <Switch
      value=self.state.toggled
      onTintColor="#DD4C39"
      onValueChange=((value) => self.reduce(() => SetSwitchValue(value), ()))
    />
};

Теперь у вас должно быть классное небольшое приложение, которое позволяет переключать переключатель в стиле ReasonML и сохранять его состояние. Милая!

Для более сложного примера сохранения и регидратации состояния JSON в ReasonML ознакомьтесь с кодом, который я написал для своего личного фитнес-трекера здесь.

Надеюсь, вы многому научились из этого поста и получили удовольствие, читая его! Чтобы узнать больше, подписывайтесь на меня в Twitter! Это самый простой способ связаться со мной и узнать о новых вещах, которые я делаю, новых идеях, которые я изучаю, или новых методах, которые я изучаю!

Всего хорошего!

- Джуван