mirror of
https://github.com/zoriya/flood.git
synced 2026-05-30 10:08:36 +00:00
connectStores: Props first, States second
React's way and the way it should be used.
This commit is contained in:
@@ -6,7 +6,7 @@ import ReactDOM from 'react-dom';
|
||||
|
||||
import detectLocale from './util/detectLocale';
|
||||
import * as i18n from './i18n/languages';
|
||||
import connectStores, {EventListenerDescriptor} from './util/connectStores';
|
||||
import connectStores from './util/connectStores';
|
||||
import AppWrapper from './components/AppWrapper';
|
||||
import AuthActions from './actions/AuthActions';
|
||||
import EventTypes from './constants/EventTypes';
|
||||
@@ -20,6 +20,10 @@ import UIStore from './stores/UIStore';
|
||||
|
||||
import '../sass/style.scss';
|
||||
|
||||
interface FloodAppProps {
|
||||
locale?: keyof typeof i18n.languages;
|
||||
}
|
||||
|
||||
const initialize = (): void => {
|
||||
UIStore.registerDependency({
|
||||
id: 'notifications',
|
||||
@@ -72,18 +76,14 @@ const appRoutes = (
|
||||
</Router>
|
||||
);
|
||||
|
||||
interface InjectedFloodAppProps {
|
||||
locale: keyof typeof i18n.languages;
|
||||
}
|
||||
|
||||
class FloodApp extends React.Component<InjectedFloodAppProps> {
|
||||
class FloodApp extends React.Component<FloodAppProps> {
|
||||
public componentDidMount(): void {
|
||||
initialize();
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let {locale} = this.props;
|
||||
if (locale === 'auto' || !Object.prototype.hasOwnProperty.call(i18n.languages, locale)) {
|
||||
if (locale == null || locale === 'auto' || !Object.prototype.hasOwnProperty.call(i18n.languages, locale)) {
|
||||
locale = detectLocale();
|
||||
}
|
||||
|
||||
@@ -95,14 +95,12 @@ class FloodApp extends React.Component<InjectedFloodAppProps> {
|
||||
}
|
||||
}
|
||||
|
||||
const ConnectedFloodApp = connectStores<InjectedFloodAppProps>(FloodApp, (): EventListenerDescriptor<
|
||||
InjectedFloodAppProps
|
||||
>[] => {
|
||||
const ConnectedFloodApp = connectStores(FloodApp, () => {
|
||||
return [
|
||||
{
|
||||
store: SettingsStore,
|
||||
event: EventTypes.SETTINGS_CHANGE,
|
||||
getValue: (): InjectedFloodAppProps => {
|
||||
getValue: () => {
|
||||
return {
|
||||
locale: SettingsStore.getFloodSettings('language'),
|
||||
};
|
||||
|
||||
@@ -13,38 +13,41 @@ interface GenericStore {
|
||||
|
||||
type Store = GenericStore | typeof AuthStore | typeof ClientStatusStore | typeof UIStore;
|
||||
|
||||
export interface EventListenerDescriptor<DerivedState, WrappedComponentProps = {}> {
|
||||
export interface EventListenerDescriptor<ConnectedComponentProps, ConnectedComponentStates> {
|
||||
store: Store;
|
||||
event: keyof typeof EventTypes | (keyof typeof EventTypes)[];
|
||||
getValue: (props: {
|
||||
payload: unknown;
|
||||
props: WrappedComponentProps;
|
||||
state: DerivedState;
|
||||
props: ConnectedComponentProps;
|
||||
state: ConnectedComponentStates;
|
||||
store: Store;
|
||||
}) => Partial<DerivedState>;
|
||||
}) => Partial<ConnectedComponentProps>;
|
||||
}
|
||||
|
||||
const connectStores = <DerivedState extends object, WrappedComponentProps extends object = {}>(
|
||||
InputComponent: React.JSXElementConstructor<WrappedComponentProps & DerivedState>,
|
||||
const connectStores = <ConnectedComponentProps extends object, ConnectedComponentStates extends object = {}>(
|
||||
InputComponent: React.JSXElementConstructor<ConnectedComponentProps & ConnectedComponentStates>,
|
||||
getEventListenerDescriptors: (
|
||||
props: WrappedComponentProps,
|
||||
) => EventListenerDescriptor<DerivedState, WrappedComponentProps>[],
|
||||
): ((props: WrappedComponentProps) => React.ReactElement<WrappedComponentProps>) => {
|
||||
class ConnectedComponent extends React.Component<WrappedComponentProps, DerivedState> {
|
||||
props: ConnectedComponentProps,
|
||||
) => EventListenerDescriptor<ConnectedComponentProps, ConnectedComponentStates>[],
|
||||
): ((props: ConnectedComponentProps) => React.ReactElement<ConnectedComponentProps>) => {
|
||||
class ConnectedComponent extends React.Component<ConnectedComponentProps, ConnectedComponentStates> {
|
||||
private eventHandlersByStore: Map<
|
||||
Store,
|
||||
Set<{events: (keyof typeof EventTypes)[]; eventHandler: (payload: unknown) => void}>
|
||||
> = new Map();
|
||||
|
||||
private constructor(props: WrappedComponentProps) {
|
||||
private constructor(props: ConnectedComponentProps) {
|
||||
super(props);
|
||||
this.state = getEventListenerDescriptors(props).reduce((state, eventListenerDescriptor): DerivedState => {
|
||||
const {store, getValue} = eventListenerDescriptor;
|
||||
return {
|
||||
...state,
|
||||
...getValue({state, props, store, payload: null}),
|
||||
};
|
||||
}, ({} as unknown) as DerivedState);
|
||||
this.state = getEventListenerDescriptors(props).reduce(
|
||||
(state, eventListenerDescriptor): ConnectedComponentStates => {
|
||||
const {store, getValue} = eventListenerDescriptor;
|
||||
return {
|
||||
...state,
|
||||
...getValue({state, props, store, payload: null}),
|
||||
};
|
||||
},
|
||||
({} as unknown) as ConnectedComponentStates,
|
||||
);
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
@@ -54,8 +57,8 @@ const connectStores = <DerivedState extends object, WrappedComponentProps extend
|
||||
const {store, event, getValue} = eventListenerDescriptor;
|
||||
const eventHandler = (payload: unknown): void =>
|
||||
this.setState(
|
||||
(state: DerivedState, props: WrappedComponentProps): DerivedState =>
|
||||
getValue({state, props, store, payload}) as DerivedState,
|
||||
(state: ConnectedComponentStates, props: ConnectedComponentProps): ConnectedComponentStates =>
|
||||
getValue({state, props, store, payload}) as ConnectedComponentStates,
|
||||
);
|
||||
const events = Array.isArray(event) ? event : [event];
|
||||
|
||||
@@ -91,11 +94,13 @@ const connectStores = <DerivedState extends object, WrappedComponentProps extend
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
return <InputComponent {...(this.props as WrappedComponentProps)} {...(this.state as DerivedState)} />;
|
||||
return (
|
||||
<InputComponent {...(this.props as ConnectedComponentProps)} {...(this.state as ConnectedComponentStates)} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (props: WrappedComponentProps): React.ReactElement<WrappedComponentProps> => {
|
||||
return (props: ConnectedComponentProps): React.ReactElement<ConnectedComponentProps> => {
|
||||
return <ConnectedComponent {...props} />;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user