Skip to main content

Сховище

Techno4 постачається з вбудованою спрощеною бібліотекою керування станом програми – Store. Вона служить централізованим сховищем для всіх компонентів програми. Ви можете використовувати спеціальні бібліотеки для керування станом, такі як Vuex для Vue, Redux для React, і використовувати функціонал вбудованого сховища Svelte. Але у випадку, якщо потрібно щось просте, Techno4 Store може добре підійти.

Створення сховища

Перш за все нам потрібно створити магазин. Створимо для цього окремий файл store.js:
// First import createStore function from Techno4 core
import { createStore } from 'Techno4';

// create store
const store = createStore({
  // start with the state (store data)
  state: {
    users: [],
    // ...
  },

  // actions to operate with state and for async manipulations
  actions: {
    // context object containing store state will be passed as an argument
    getUsers({ state }) {
      // fetch users from API
      fetch('some-url')
        .then((res) => res.json())
        .then((users) => {
          // assign new users to store state.users
          state.users = users;
        })
    },
    // ...
  },

  // getters to retrieve the state
  getters: {
    // context object containing store state will be passed as an argument
    users({ state }) {
      return state.users;
    }
  }

})

// export store
export default store;
Якщо ви не використовуєте модулі, то файл store.js матиме такий вигляд:
// save store as global object
window.store = Techno4.createStore({
  state: { /* ... */ },
  actions: { /* ... */ },
  getters: { /* ... */ },
})
У цьому прикладі ми використали таку функцію API:

createStore(storeParameters)- створити сховище.

storeParameters - object. Об'єкт із збереженням параметрів.

Метод повертає створений екземпляр сховища

Параметри сховища

Тепер розглянемо об’єкт storeParameters:

Стан

state — єдиний об’єкт, який містить увесь стан рівня програми та служить «єдиним джерелом істини». Це також означає, що зазвичай ви матимете лише одне сховище для кожної програми. Єдине дерево станів спрощує пошук конкретної частини стану та дозволяє нам легко робити знімки поточного стану програми для цілей налагодження.

Дії

actions використовуються для зміни стану, для . Обробники дій отримують об’єкт контексту зі станом зберігання та методом відправки для виклику інших дій. Тож ви можете отримати доступ до context.store, щоб отримати доступ до стану, або викликати інші дії за допомогою context.dispatch.

Як другий аргумент обробники дій можуть отримувати будь-які спеціальні дані.

Щоб підтримувати реактивність сховища, модифікацію стану слід виконувати за допомогою призначення. Наприклад:
// modification of current state property - NOT REACTIVE
state.users.push(...users);

// assignemt to new value - REACTIVE
state.users = [...state.users, ...users];

Здобувачі (геттери)

getters обробники використовуються для повернення даних із стану зберігання. Також зручно, коли нам потрібно обчислити похідний стан на основі стану сховища, наприклад, фільтруючи список елементів:
const store = createStore({
  state: {
    users: [
      { id: 1, name: '...', registered: true },
      { id: 2, name: '...', registered: false }
    ]
  },
  getters: {
    registeredUsers: ({ state }) => {
      return state.users.filter((user) => user.registered);
    }
  }
})
Обробники геттера також отримують об’єкт контексту, але лише зі станом зберігання. Неможливо, наприклад, викликати інші дії з геттерів.

Використання сховища

Тепер, коли ми створили наше сховище, дізнаймось, як ним користуватися. Перш за все, нам потрібно передати створене сховище до основного екземпляра програми:
// import our store
import store from 'path/to/store.js';

const app = new Techno4({
  // pass store to the app's "store" parameter
  store,
  ...
})

Доступ до сховища та стану

Можна отримати прямий доступ до сховища (і його стану), посилаючись на створений нами екземпляр сховища:
import store from 'path/to/store.js';

console.log(store.state.users);
Або за допомогою доступу до властивості магазину екземпляра Techno4:
import store from 'path/to/store.js';

const app = new Techno4({
  store,
  ...
})

// somewhere later
console.log(app.store.state.users);

Диспетчерські дії

Щоб викликати дію, нам потрібно викликати метод store.dispatch із назвою дії, яку потрібно викликати. Якщо у нас є така дія магазину:
const store = createStore({
  // ...
  actions: {
    // handler receives custom data in second argument
    getUsers({ state }, { total }) {
      fetch(`some-url?total=${total}`)
        .then((res) => res.json())
        .then((users) => {
          state.users = users;
        })
    },
  },
  // ...
})
Ми повинні викликати метод store.dispatch:
import store from 'path/to/store.js';

// call 'getUsers' actions
store.dispatch('getUsers', { total: 10 })
Якщо в обробнику дії ми хочемо викликати інший обробник дії:
const store = createStore({
  // ...
  actions: {
    setLoading({ state }, isLoading) {
      state.isLoading = isLoading;
    },
    // handler context also contains "dispatch" method
    getUsers({ state, dispatch }, { total }) {
      // call other action
      dispatch('setLoading', true);
      fetch(`some-url?total=${total}`)
        .then((res) => res.json())
        .then((users) => {
          state.users = users;
          // call other action
          dispatch('setLoading', false);
        })
    },
  },
  // ...
});

Здобувачі (геттери)

Значення getters можна отримати як статичні властивості об’єкта store.getters.
const store = createStore({
  state: {
    count: 10,
  },
  getters: {
    count({ state }) {
      return state.count;
    },
    double({ state }) {
      return state.count * 2;
    },
  },
});
import store from 'path/to/store.js';

const count = store.getters.count;
const double = store.getters.double;
Значення getter — це статичний об’єкт із властивістю .value, що містить результат обробки геттерів, отже:
console.log(count.value); // -> 10
console.log(double.value); // -> 20
Геттери, на відміну від стану, призначені бути реактивними. Отже, коли вам не потрібна жодна реактивність, ви можете просто отримати доступ до store.state напряму, інакше використовуйте геттери.

Використання з компонентами маршрутизатора

Контекст компонента маршрутизатора має властивість $store із такими властивостями:

state - зі станом сховища

dispatch - викликати дії

getters- для доступу до обробників реактивних геттерів

Якщо у нас є таке сховище :
const store = createStore({
  state: {
    users: [],
  },
  actions: {
    getUsers({ state }) {
      // ...
    },
  },
  getters: {
    users({ state }) {
      return state.users;
    }
  },
});
Тоді, наприклад, ми повинні використовувати наступне в компоненті Router:
<template>
  <div class="page">
    <ul>
      <!-- getter has value in ".value" property -->
      ${users.value.map((user) => $h`
        <li>${user.name}</li>
      `)}
    </ul>
  </div>
</template>
<script>
  export default (props, { $store, $on }) => {
    // retrieve "users" getter handler value. Initially empty array
    const users = $store.getters('users');

    $on('pageInit', () => {
      // load users on page init
      $store.dispatch('getUsers');
    });

    return $render;
  }
</script>

Приклади

import { createStore } from 'store';

const store = createStore({
  state: {
    loading: false,
    users: [],
  },
  actions: {
    getUsers({ state }) {
      state.loading = true;
      setTimeout(() => {
        state.users = ['User 1', 'User 2', 'User 3', 'User 4', 'User 5'];
        state.loading = false;
      }, 3000);
    },
  },
  getters: {
    loading({ state }) {
      return state.loading;
    },
    users({ state }) {
      return state.users;
    },
  },
});

export default store;
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="title">Store</div>
      </div>
    </div>
    <div class="page-content">
      ${users.value.length > 0 ? $h`
        <div class="list">
          <ul>
            ${users.value.map((user) => $h`
            <li class="item-content">
              <div class="item-inner">
                <div class="item-title">${user}</div>
              </div>
            </li>
            `)}
          </ul>
        </div>
      ` : $h`
        <div class="block block-strong">
          <a
            href="#"
            class="button button-fill button-preloader ${loading.value ? 'button-loading' : ''}"
            @click=${loadUsers}
          >
            <span class="preloader"></span>
            <span>Load Users</span>
          </a>
        </div>
      `}
    </div>
  </div>
</template>
<script>
  export default (props, { $store }) => {
    const loading = $store.getters.loading;
    const users = $store.getters.users;

    const loadUsers = () => {
      $store.dispatch('getUsers');
    };

    return $render;
  };
</script>