Nikita Kirsanov, EPAM Systems (Ryazan)
Nikita Kirsanov - Senior Software Engineer - EPAM Systems (Ryazan)
function newMessageHandler(newMessage) {
var chatTab = ChatTabs.getChatTab(message.threadId);
chatTab.appendMessage(message)
}
function newMessageHandler(newMessage) {
UnseenCount.increment();
var chatTab = ChatTabs.getChatTab(message.threadId);
chatTab.appendMessage(message)
if (chatTab.hasFocus())
UnseenCount.decrement();
}
function newMessageHandler(newMessage) {
UnseenCount.increment();
var chatTab = ChatTabs.getChatTab(message.threadId);
chatTab.appendMessage(message)
var messageView = Messages.getOpenView();
var threadId = messageView.getThreadId();
if (threadId === message.threadId)
messageView.appendMessage(message)
if (chatTab.hasFocus() || threadId === message.threadId)
UnseenCount.decrement();
}
Redux evolves the ideas of Flux, but avoids its complexity by taking cues from Elm.
The state of your whole application is stored in an object tree within a single store.
import { createStore } from 'redux';
const store = createStore(...);
store.subscribe(
() => console.log(store.getState())
)
The only way to change the state is to emit an action, an object describing what happened.
store.dispatch({
type: 'ANY_ACTION',
payload: 'some data goes here'
error: false // to deliver error
})
To specify how the state tree is transformed by actions, you write pure reducers.
function myReducer(state = {}, action) {
switch (action.type) {
case 'ACTION_I_KNOW_HOW_TO_PROCESS':
return { ...state, some: change }
default:
return state;
}
<body>
<span class="js-value">0</span>
<br/>
<button class="js-increment-btn">+</button>
<button class="js-decrement-btn">-</button>
</body>
// index.js
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer);
document.querySelector('.js-increment')
.click = () => store.dispatch('INCREMENT')
...
var valueEl = document.querySelector('.js-value');
store.subscribe(
() => valueEl.innerHTML = store.getState().toString()
);
// reducer.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
// avoid
const messageReducer = (state, action) => {
switch (action.type) {
case 'NEW_MESSAGE':
return {
...state,
messages: state.messages.concat([action.payload]),
unreadMessages: state.unreadMessages + 1,
}
default:
return state;
}
// recommended
const messageReducer = (state, action) => {
switch (action.type) {
case 'NEW_MESSAGE': ...
case 'MESSAGE_WAS_READ':
return {
messages: state.messages.map(message => {
if (message.id === action.payload) {
return { ...message, read: true }
}
return message
}),
}
}
import { createSelector } from 'reselect';
const messagesSelector = state => state.messages
const unreadMessages = createSelector(messages,
messagesSelector,
messages => messages.filter(m => !m.read)
)
const unreadMessagesCount => createSelector(
unreadMessages,
messages => messages.length
)
It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.
With a help of it you can hold side effects separately.
export function asyncAction(idOfSomething) {
return dispatch => {
dispatch({ type: 'START_FETCHING_SOMETHING' });
fetch(`/something/${idOfSomething}`)
.then(response => dispatch({
type: 'FETCHED_SOMETHING',
payload: response
}));
};
}
Rx.Observable.fromEvent(this.refs.input, 'input')
.map(event => event.target.value)
.filter(value => !!value)
.debounceTime(500)
.switchMap(searchTerm =>
ajax('/api/search', searchTerm)
.map(payload => ({ type: 'QUERY_FULLFILLED', payload }))
.catch(payload => ({
type: 'QUERY_FULLFILLED',
error: true,
payload
}))