Мы собираемся реализовать нашу собственную потоковую архитектуру, вдохновленную редуксом, и посмотрим, как переделка шаблонов и некоторый синтаксический сахар могут сократить шаблонное обеспечение безопасности типов.
Рабочий закомментированный код здесь https://github.com/freddi301/cinammon-redux
Примеры запуска здесь https://cinammon-redux.now.sh
// @flow
Прежде всего, давайте посмотрим на редуктор
Далее следует очень простая реализация
type CounterAction = { type: 'inc' } | { type: 'add', payload: number } ; type CounterState = number; const CounterReducer: Reducer<CounterState, CounterAction> = (state, action) => // implement the logic { switch (action.type) { case 'inc': return state + 1; case 'add': return state + action.payload; default: throw new Error(`unknown action: ${action.type}`); } }
Вот и все, но теперь нам нужен изменяемый объект, который сохранит наше состояние.
Простой шаблон наблюдателя подойдет
export class Store<State, Action> { state: State; reducer: (state: State, action: Action) => State; replaceReducer(reducer: (state: State, action: Action) => State) { this.reducer = reducer; }; listeners: Set<(state: State) => void> = new Set; constructor(state: State, reducer: (state: State, action: Action) => State) { this.state = state; this.reducer = reducer; } publish = (action: Action): void => { this.state = this.reducer(this.state, action); this.notify(); } notify() { for (let listener of this.listeners) listener(this.state); } subscribe(listener: (state: State) => void): void { this.listeners.add(listener); } unsubscribe(listener: (state: State) => void): void { this.listeners.delete(listener); } }
Поскольку архитектура потока в основном используется с реакцией, давайте создадим компонент высокого порядка для прослушивания хранилища. Эта конвенция будет соблюдаться.
import React from 'react'; export function connect<State, Action, Props>( store: Store<State, Action>, component: (props: Props, state: State publish: (action: Action) => void) => React.Element<*> ): Class<React.Component<void, Props, { state: State }>> { return class extends React.Component<void, Props, { state: State }> { store: Store<State, Action> = store; component: ( props: Props, state: State, publish: (action: Action) => void ) => React.Element<*> = component; state = { state: store.state }; render() { return this.component( this.props, this.state.state, this.store.publish ); } listen = (state: State) => this.setState({ state }); componentWillMount() { this.store.subscribe(this.listen); } componentWillUnmount() { this.store.unsubscribe(this.listen); } } }
Мы не собираемся использовать внутреннее состояние, вместо этого мы реифицируем домен и состояние просмотра в хранилище. См. также вязовую архитектуру.
const counterDemoStore = new Store(0, CounterReducer); const CounterComponent = (props, state, publish) => <div> counter = {state}<br/> <button onClick={() => publish({ type: 'inc' })}>inc</button><br/> </div>; export const CounterDemo = connect( counterDemoStore, CounterComponent );
Продолжение следует :)