Key Words:
Thư viện React và Redux:
ES6:
Sẽ gồm 4 thành phần chính:
View
và Actions
sẽ có liên kết gọi là Dispatch
. Dispatch
là ghi nhận thao tác của người dùng tác động lên giao diện View.
dispatch
trên View
, thì luồng dữ liệu sẽ truy cập vào Actions
lấy ra mô tả hành động action
tương ứng, sau đó chuyển thông tin action
này sang Reduce
xử lý.store
.state
ứng dụng, tại đây chỉ có chức năng getValue(state).function
nguyên thủy(pure function
) gọi là reduce
;
State hiện tại
và thông tin Action
, sau đó trả về một State mới
để cập nhật vào store
.
Predictable
, tức là cùng 1 state, cùng 1 action thì nó luôn luôn cho ra 1 state mới giống nhau, luôn luôn là như vậy.Store
được update do có thêm state
mới sau xử lý reduce
, thì thành phần view
cần được update lại, khi này sẽ dùng chức năng subscribe
. Ta có thể gọi đây là chức năng Render
dữ liệu từ Store
cho VIEW
.
Store
bao gồm các State
(current) sẽ được Render
, và hiển thị trên View
.View
, người dùng thao tác trên giao diện này được gọi là các dispatch
.State
(current) và dispatch
sẽ được đưa đến thành phần Actions
chứa các mô tả về các loại action
để nhận biết dispatch
đó thuộc action
nào.action
, luồng dữ liệu gồm State
(current) và action
sẽ được chuyển tiếp đến thành phần Reducer
tương ứng action
xử lý.Reducer
sẽ xử lý các State
(current) thành State
(new) và cập nhật vào store
.store
được cập nhật State
(new) , nên View
sẽ cần được Render
lại để cập nhật.
index.html
và phần dữ liệu sẽ không viết các element thành phần chi tiết mà sẽ dùng kiểu CSR (Client Side Rendering) thao tác dùng DOM
để chọn element id ='root'
để truyền dữ liệu render
vào đây.<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Todo App</title>
</head>
<body>
<div id="root"></div>
</body>
<script type="module" src="./script.js"></script>
</html>
index.html
sẽ link với file script.js
theo dạng module : ` `.store
sẽ bao gồm các state
trạng thái chứa dữ liệu, nên ở đây ta sẽ viết 1 hàm để xử lý chuyển 1 mảng danh sách state
, truyền vào element #root
như sau:
ES6 - Tagged template literals
như sau:const rootElement = document.querySelector('#root');
const cars = ['BMW', 'Porsche', 'Mercedes'];
const html = `
<h1>TODO List</h1>
<ul>
${cars.map((car) => `<li>${car}</li>`).join('')}
<ul>
`;
console.log(html);
rootElement.innerHTML = html;
const rootElement = document.querySelector('#root');
const cars = ['BMW', 'Porsche', 'Mercedes', true, undefined, null, NaN, false];
const isSucceeded = false;
const html = `
<h1>${isSucceeded} && TODO List</h1>
<ul>
${cars.map((car) => `<li>${car}</li>`).join('')}
<ul>
`;
console.log(html);
rootElement.innerHTML = html;
export default function html([first, ...values], ...strings) {
return values
.reduce(
(acc, cur) => {
return acc.concat(strings.shift(), cur);
},
[first]
)
.filter((x) => (x && x !== true) || x === 0)
.join('');
}
import html from './core.js';
const cars = ['BMW', 'Porsche', 'Mercedes'];
const isSucceeded = false;
const output = html`
<h1>${isSucceeded} && TODO List</h1>
<ul>
${cars.map((car) => `<li>${car}</li>`)}
<ul></ul>
</ul>
`;
const rootElement = document.querySelector('#root');
rootElement.innerHTML = output;
html()
được gọi là template view
// core.js LIBRARY
function html([first, ...strings], ...values) {
return values
.reduce((acc, cur) => acc.concat(cur, strings.shift()), [first])
.filter((x) => (x && x !== true) || x === 0)
.join('');
}
function createStore(dataAndAction) {
let state = dataAndAction();
const roots = new Map();
function render() {
for (const [root, component] of roots) {
const output = component();
root.innerHTML = output;
}
}
return {
// enter action from user: add, edit, delete...
dispatch(action, ...args) {
state = reducer(state, action, args);
render();
},
// get data, action and use action to process data
connect: function connect(
selector = function selector(state) {
return state;
}
) {
return function (component) {
return function (props, ...args) {
return component(Object.assign({}, props, selector(state), ...args));
};
};
},
// display data to user-interface
attach(component, root) {
roots.set(root, component);
render();
},
};
}
// DATA
var init = {
cars: ['toyota', 'honda', 'porsche'],
};
// reducer.js getDATA and createACTIONs
export default function reducer(action, state = init, ...args) {
console.log(action);
switch (action) {
case 'ADD': // action add, edit, delete...
let [newCar] = args;
return {
cars: [...state.cars, newCar],
};
break;
case 'DEL': // action add, edit, delete...
const index = args[0]
console.log('args::', index,state);
if (index > -1) {
state.cars.splice(index, 1); // 2nd parameter means remove one item only
}
return state;
break;
default:
return state;
}
}
function reducer(state = init, action, args) {
switch (action) {
case 'ADD': // action add, edit, delete...
const [newCar] = args;
return {
cars: [...state.cars, newCar],
};
default:
return state;
}
}
// store.js USING LIBRARY
const { attach, connect, dispatch } = createStore(reducer);
window.dispatch = dispatch;
// Cycle: dispatch -> connect -> attach -> dispatch -> connect -> ...
// App.js
var App = connect()(
// using connect, explaining role of connect here
function ({ cars }) {
// using html
return html`
<ul>
${cars.map((car) => `<li>${car}</li>`)}
</ul>
<button onclick="dispatch('ADD', 'PORSCHE')">Add car</button>
`;
// using dispatch-global
}
);
// script.js
attach(App, document.getElementById('root')); // using attach
// THE END
// Good luck! You can do it :D