Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref(ts): Introduce CommonStoreInterface for useLegacyStore #29131

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions static/app/stores/configStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import Reflux from 'reflux';

import {Config} from 'app/types';

type ConfigStoreInterface = {
import {CommonStoreInterface} from './types';

type ConfigStoreInterface = CommonStoreInterface<Config> & {
get<K extends keyof Config>(key: K): Config[K];
set<K extends keyof Config>(key: K, value: Config[K]): void;
getConfig(): Config;
Expand Down Expand Up @@ -48,10 +50,6 @@ const storeConfig: Reflux.StoreDefinition & Internals & ConfigStoreInterface = {
this.set('theme', theme);
},

getConfig() {
return this.config;
},

loadInitialData(config): void {
const shouldUseDarkMode = config.user?.options.theme === 'dark';

Expand All @@ -69,6 +67,14 @@ const storeConfig: Reflux.StoreDefinition & Internals & ConfigStoreInterface = {

this.trigger(config);
},

getConfig() {
return this.config;
},

getState() {
return this.config;
},
};

const ConfigStore = Reflux.createStore(storeConfig) as Reflux.Store &
Expand Down
13 changes: 7 additions & 6 deletions static/app/stores/indicatorStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import {Indicator} from 'app/actionCreators/indicator';
import IndicatorActions from 'app/actions/indicatorActions';
import {t} from 'app/locale';

type IndicatorStoreInterface = {
import {CommonStoreInterface} from './types';

type IndicatorStoreInterface = CommonStoreInterface<Indicator[]> & {
init(): void;
get(): Indicator[];
addSuccess(message: string): Indicator;
addError(message?: string): Indicator;
/**
Expand Down Expand Up @@ -69,10 +70,6 @@ const storeConfig: Reflux.StoreDefinition & Internals & IndicatorStoreInterface
this.listenTo(IndicatorActions.clear, this.clear);
},

get() {
return this.items;
},

addSuccess(message) {
return this.add(message, 'success', {duration: 2000});
},
Expand Down Expand Up @@ -136,6 +133,10 @@ const storeConfig: Reflux.StoreDefinition & Internals & IndicatorStoreInterface

this.trigger(this.items);
},

getState() {
return this.items;
},
};

const IndicatorStore = Reflux.createStore(storeConfig) as Reflux.Store &
Expand Down
8 changes: 7 additions & 1 deletion static/app/stores/organizationStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {ORGANIZATION_FETCH_ERROR_TYPES} from 'app/constants';
import {Organization} from 'app/types';
import RequestError from 'app/utils/requestError/requestError';

import {CommonStoreInterface} from './types';

type UpdateOptions = {
replace?: boolean;
};
Expand All @@ -17,7 +19,7 @@ type State = {
error?: RequestError | null;
};

type OrganizationStoreInterface = {
type OrganizationStoreInterface = CommonStoreInterface<State> & {
init(): void;
reset(): void;
onUpdate(org: Organization, options: UpdateOptions): void;
Expand Down Expand Up @@ -79,6 +81,10 @@ const storeConfig: Reflux.StoreDefinition & OrganizationStoreInterface = {
dirty: this.dirty,
};
},

getState() {
return this.get();
},
};

const OrganizationStore = Reflux.createStore(storeConfig) as Reflux.Store &
Expand Down
7 changes: 4 additions & 3 deletions static/app/stores/teamStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import Reflux from 'reflux';
import TeamActions from 'app/actions/teamActions';
import {Team} from 'app/types';

import {CommonStoreInterface} from './types';

const MAX_TEAMS = 100;

type State = {
Expand All @@ -12,15 +14,14 @@ type State = {
loadedUserTeams: boolean;
};

type TeamStoreInterface = {
type TeamStoreInterface = CommonStoreInterface<State> & {
initialized: boolean;
state: State;
reset(): void;
loadInitialData(items: Team[], hasMore?: boolean | null): void;
onUpdateSuccess(itemId: string, response: Team): void;
onRemoveSuccess(slug: string): void;
onCreateSuccess(team: Team): void;
get(): State;
getAll(): Team[];
getById(id: string): Team | null;
getBySlug(slug: string): Team | null;
Expand Down Expand Up @@ -130,7 +131,7 @@ const teamStoreConfig: Reflux.StoreDefinition & TeamStoreInterface = {
this.loadInitialData([...this.state.teams, team]);
},

get() {
getState() {
return this.state;
},

Expand Down
12 changes: 12 additions & 0 deletions static/app/stores/types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* All stores implementing this interface have a common getState which returns
* the stores state.
*
* When a store implements this it becomes usable with the `useLegacyStore` hook.
*/
export type CommonStoreInterface<T> = {
/**
* Returns the current state represented within the store
*/
getState(): T;
};
15 changes: 8 additions & 7 deletions static/app/stores/useLegacyStore.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {useEffect, useState} from 'react';
import Reflux from 'reflux';

type LegacyStoreShape = Reflux.Store & {
// Store must have `get` function that returns the current state
get(): any;
};
import {CommonStoreInterface} from './types';

type LegacyStoreShape = Reflux.Store & CommonStoreInterface<any>;

/**
* Returns the state of a reflux store. Automatically unsubscribes when destroyed
Expand All @@ -15,10 +14,12 @@ type LegacyStoreShape = Reflux.Store & {
*/
export function useLegacyStore<T extends LegacyStoreShape>(
store: T
): ReturnType<T['get']> {
const [state, setState] = useState(store.get());
): ReturnType<T['getState']> {
const [state, setState] = useState(store.getState());

// Not all stores emit the new state, call get on change
const callback = () => setState(store.get());
const callback = () => setState(store.getState());

useEffect(() => store.listen(callback, undefined) as () => void, []);

return state;
Expand Down
20 changes: 10 additions & 10 deletions tests/js/spec/stores/teamStore.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('TeamStore', function () {
});

it('populate teams correctly', async function () {
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [],
loading: true,
hasMore: null,
Expand All @@ -24,7 +24,7 @@ describe('TeamStore', function () {

TeamActions.loadTeams([teamFoo, teamBar]);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [teamBar, teamFoo],
loading: false,
hasMore: null,
Expand All @@ -33,14 +33,14 @@ describe('TeamStore', function () {
});

it('loads user teams', async function () {
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [],
loadedUserTeams: false,
});

TeamActions.loadUserTeams([teamFoo]);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [teamFoo],
loadedUserTeams: true,
});
Expand All @@ -51,41 +51,41 @@ describe('TeamStore', function () {
it('adds new teams', async function () {
TeamActions.loadTeams([teamFoo]);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [teamFoo],
});

TeamActions.createTeamSuccess(teamBar);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [teamBar, teamFoo],
});
});

it('removes teams', async function () {
TeamActions.loadTeams([teamFoo]);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [teamFoo],
});

TeamActions.removeTeamSuccess(teamFoo.slug);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [],
});
});

it('updates teams', async function () {
TeamActions.loadTeams([teamFoo]);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [teamFoo],
});

TeamActions.updateSuccess(teamFoo.slug, teamBar);
await tick();
expect(TeamStore.get()).toMatchObject({
expect(TeamStore.getState()).toMatchObject({
teams: [teamBar],
});
});
Expand Down