You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/sr/7-bank-project/4-state-management
Lee Stott 2daab5271b
Update Quiz Link
3 weeks ago
..
README.md Update Quiz Link 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

Изградња апликације за банкарство, део 4: Концепти управљања стањем

Квиз пре предавања

Квиз пре предавања

Увод

Како веб апликација расте, постаје изазов пратити све токове података. Који код добија податке, која страница их користи, где и када треба да се ажурирају... лако је завршити са неуредним кодом који је тешко одржавати. Ово је посебно тачно када је потребно делити податке између различитих страница апликације, на пример корисничке податке. Концепт управљања стањем увек је постојао у свим врстама програма, али како веб апликације постају све сложеније, сада је то кључна тачка о којој треба размишљати током развоја.

У овом завршном делу, размотрићемо апликацију коју смо изградили како бисмо преиспитали начин управљања стањем, омогућавајући подршку за освежавање прегледача у било ком тренутку и чување података током корисничких сесија.

Предуслови

Потребно је да сте завршили преузимање података део веб апликације за ову лекцију. Такође, потребно је да инсталирате Node.js и покренете сервер API локално како бисте могли да управљате подацима о налогу.

Можете тестирати да ли сервер ради исправно извршавањем ове команде у терминалу:

curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result

Преиспитивање управљања стањем

У претходној лекцији, увели смо основни концепт стања у нашој апликацији са глобалном променљивом account која садржи банкарске податке за тренутно пријављеног корисника. Међутим, наша тренутна имплементација има неке недостатке. Покушајте да освежите страницу док сте на контролној табли. Шта се дешава?

Постоје три проблема са тренутним кодом:

  • Стање није сачувано, јер освежавање прегледача враћа вас на страницу за пријаву.
  • Постоји више функција које мењају стање. Како апликација расте, то може отежати праћење промена и лако је заборавити да се нешто ажурира.
  • Стање није очишћено, па када кликнете на Одјава, подаци о налогу су и даље ту, иако сте на страници за пријаву.

Могли бисмо ажурирати наш код да решимо ове проблеме један по један, али то би створило више дуплирања кода и учинило апликацију сложенијом и тежом за одржавање. Или бисмо могли паузирати на неколико минута и преиспитати нашу стратегију.

Које проблеме заиста покушавамо да решимо овде?

Управљање стањем се односи на проналажење доброг приступа за решавање ова два конкретна проблема:

  • Како учинити токове података у апликацији разумљивим?
  • Како одржати податке о стању увек синхронизованим са корисничким интерфејсом (и обрнуто)?

Када се позабавите овим питањима, било који други проблеми које можда имате могу бити већ решени или постати лакши за решавање. Постоји много могућих приступа за решавање ових проблема, али ми ћемо се определити за уобичајено решење које се састоји од централизовања података и начина њиховог мењања. Токови података би изгледали овако:

Шема која приказује токове података између HTML-а, корисничких акција и стања

Овде нећемо покривати део где подаци аутоматски покрећу ажурирање приказа, јер је то повезано са напреднијим концептима реактивног програмирања. То је добра тема за дубље истраживање ако сте заинтересовани.

Постоји много библиотека са различитим приступима управљању стањем, Redux је популарна опција. Погледајте концепте и обрасце који се користе, јер је то често добар начин да научите о потенцијалним проблемима са којима се можете суочити у великим веб апликацијама и како их можете решити.

Задатак

Почећемо са мало рефакторисања. Замените декларацију account:

let account = null;

Са:

let state = {
  account: null
};

Идеја је да централизујемо све податке наше апликације у један објекат стања. Тренутно имамо само account у стању, па се много не мења, али то ствара пут за будуће еволуције.

Такође морамо ажурирати функције које га користе. У функцијама register() и login(), замените account = ... са state.account = ...;

На врху функције updateDashboard() додајте ову линију:

const account = state.account;

Ово рефакторисање само по себи није донело много побољшања, али идеја је била да се постави основа за следеће промене.

Праћење промена података

Сада када смо поставили објекат state за чување наших података, следећи корак је централизовање ажурирања. Циљ је да се олакша праћење било каквих промена и када се оне дешавају.

Да бисмо избегли промене направљене на објекту state, такође је добра пракса да га сматрамо непроменљивим, што значи да се уопште не може мењати. То такође значи да морате креирати нови објекат стања ако желите да промените било шта у њему. На овај начин, градите заштиту од потенцијално нежељених споредних ефеката, и отварате могућности за нове функције у вашој апликацији, као што је имплементација undo/redo, док такође олакшавате дебаговање. На пример, могли бисте да бележите сваку промену направљену на стању и чувате историју промена како бисте разумели извор грешке.

У JavaScript-у можете користити Object.freeze() да креирате непроменљиву верзију објекта. Ако покушате да направите промене на непроменљивом објекту, биће подигнут изузетак.

Да ли знате разлику између плитког и дубоког непроменљивог објекта? Можете прочитати о томе овде.

Задатак

Хајде да креирамо нову функцију updateState():

function updateState(property, newData) {
  state = Object.freeze({
    ...state,
    [property]: newData
  });
}

У овој функцији, креирамо нови објекат стања и копирамо податке из претходног стања користећи spread (...) оператор. Затим замењујемо одређено својство објекта стања новим подацима користећи нотацију са заградама [property] за доделу. На крају, закључавамо објекат како бисмо спречили модификације користећи Object.freeze(). Тренутно имамо само својство account у стању, али са овим приступом можете додати онолико својстава колико вам је потребно у стању.

Такође ћемо ажурирати иницијализацију state како бисмо били сигурни да је почетно стање такође замрзнуто:

let state = Object.freeze({
  account: null
});

Након тога, ажурирајте функцију register заменом state.account = result; са:

updateState('account', result);

Урадите исто са функцијом login, заменом state.account = data; са:

updateState('account', data);

Сада ћемо искористити прилику да решимо проблем са подацима о налогу који нису очишћени када корисник кликне на Одјава.

Креирајте нову функцију logout():

function logout() {
  updateState('account', null);
  navigate('/login');
}

У updateDashboard(), замените преусмеравање return navigate('/login'); са return logout();

Покушајте да региструјете нови налог, одјавите се и поново пријавите да бисте проверили да ли све и даље ради исправно.

Савет: можете погледати све промене стања додавањем console.log(state) на крају updateState() и отварањем конзоле у алатима за развој вашег прегледача.

Чување стања

Већини веб апликација је потребно да чувају податке како би исправно функционисале. Сви критични подаци обично се чувају у бази података и приступа им се преко серверског API-ја, као што су подаци о корисничком налогу у нашем случају. Али понекад је такође занимљиво чувати неке податке на клијентској апликацији која ради у вашем прегледачу, ради бољег корисничког искуства или ради побољшања перформанси учитавања.

Када желите да чувате податке у вашем прегледачу, постоји неколико важних питања која треба да поставите себи:

  • Да ли су подаци осетљиви? Требало би избегавати чување било каквих осетљивих података на клијенту, као што су корисничке лозинке.
  • Колико дуго вам је потребно да задржите ове податке? Да ли планирате да приступите овим подацима само током тренутне сесије или желите да буду трајно сачувани?

Постоји више начина за чување информација унутар веб апликације, у зависности од тога шта желите да постигнете. На пример, можете користити URL-ове за чување упита за претрагу и учинити их деливим између корисника. Такође можете користити HTTP колачиће ако подаци треба да се деле са сервером, као што су информације о аутентификацији.

Друга опција је коришћење једног од многих API-ја прегледача за чување података. Два од њих су посебно занимљива:

  • localStorage: Key/Value store који омогућава чување података специфичних за тренутни веб сајт током различитих сесија. Подаци сачувани у њему никада не истичу.
  • sessionStorage: ради исто као localStorage, осим што се подаци сачувани у њему бришу када сесија заврши (када се прегледач затвори).

Имајте на уму да оба ова API-ја дозвољавају само чување стрингова. Ако желите да чувате сложене објекте, мораћете да их серијализујете у JSON формат користећи JSON.stringify().

Ако желите да креирате веб апликацију која не ради са сервером, могуће је креирати базу података на клијенту користећи IndexedDB API. Овај је резервисан за напредне случајеве употребе или ако треба да чувате значајну количину података, јер је сложенији за употребу.

Задатак

Желимо да наши корисници остану пријављени док експлицитно не кликну на дугме Одјава, па ћемо користити localStorage за чување података о налогу. Прво, хајде да дефинишемо кључ који ћемо користити за чување наших података.

const storageKey = 'savedAccount';

Затим додајте ову линију на крај функције updateState():

localStorage.setItem(storageKey, JSON.stringify(state.account));

Са овим, подаци о корисничком налогу ће бити сачувани и увек ажурирани, јер смо претходно централизовали сва наша ажурирања стања. Овде почињемо да уживамо у предностима свих наших претходних рефакторисања 🙂.

Како су подаци сачувани, такође морамо водити рачуна о њиховом враћању када се апликација учита. Пошто ћемо почети да имамо више кода за иницијализацију, можда је добра идеја да креирамо нову функцију init, која такође укључује наш претходни код на крају app.js:

function init() {
  const savedAccount = localStorage.getItem(storageKey);
  if (savedAccount) {
    updateState('account', JSON.parse(savedAccount));
  }

  // Our previous initialization code
  window.onpopstate = () => updateRoute();
  updateRoute();
}

init();

Овде враћамо сачуване податке, и ако их има, ажурирамо стање у складу с тим. Важно је то урадити пре ажурирања руте, јер може постојати код који се ослања на стање током ажурирања странице.

Такође можемо учинити страницу Контролна табла подразумеваном страницом наше апликације, јер сада чувамо податке о налогу. Ако се не пронађу подаци, контролна табла се брине о преусмеравању на страницу Пријава. У updateRoute(), замените резервну return navigate('/login'); са return navigate('/dashboard');.

Сада се пријавите у апликацију и покушајте да освежите страницу. Требало би да останете на контролној табли. Са тим ажурирањем, решили смо све наше почетне проблеме...

Освежавање података

...Али можда смо такође створили нови проблем. Опа!

Идите на контролну таблу користећи налог test, а затим покрените ову команду у терминалу да бисте креирали нову трансакцију:

curl --request POST \
     --header "Content-Type: application/json" \
     --data "{ \"date\": \"2020-07-24\", \"object\": \"Bought book\", \"amount\": -20 }" \
     http://localhost:5000/api/accounts/test/transactions

Покушајте да освежите страницу контролне табле у прегледачу сада. Шта се дешава? Да ли видите нову трансакцију?

Стање је сачувано на неодређено време захваљујући localStorage, али то такође значи да се никада не ажурира док се не одјавите из апликације и поново пријавите!

Једна могућа стратегија за решавање овог проблема је да се подаци о налогу поново учитавају сваки пут када се контролна табла учита, како би се избегли застарели подаци.

Задатак

Креирајте нову функцију updateAccountData:

async function updateAccountData() {
  const account = state.account;
  if (!account) {
    return logout();
  }

  const data = await getAccount(account.user);
  if (data.error) {
    return logout();
  }

  updateState('account', data);
}

Ова метода проверава да ли смо тренутно пријављени, а затим поново учитава податке о налогу са сервера.

Креирајте још једну функцију под називом refresh:

async function refresh() {
  await updateAccountData();
  updateDashboard();
}

Ова функција ажурира податке о налогу Квиз након предавања

Задатак

Имплементирајте дијалог "Додај трансакцију"

Ево примера резултата након завршетка задатка:

Снимак екрана који приказује пример дијалога "Додај трансакцију"


Одрицање од одговорности:
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције Co-op Translator. Иако се трудимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на његовом изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не преузимамо одговорност за било каква погрешна тумачења или неспоразуме који могу настати услед коришћења овог превода.