Skip to content

Commit

Permalink
move api calls into actions fixes mozilla#2957 (mozilla#3041)
Browse files Browse the repository at this point in the history
* move api calls into actions fixes mozilla#2957

* remove commented out code

* types

* typing

Co-authored-by: Jared Lockhart <[email protected]>
  • Loading branch information
tiftran and jaredlockhart authored Jul 14, 2020
1 parent 185bb57 commit 29d1431
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 60 deletions.
33 changes: 8 additions & 25 deletions app/experimenter/static/rapid/components/forms/ExperimentForm.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React from "react";
import { useHistory, useParams } from "react-router-dom";

import {
saveExperiment,
updateExperiment,
} from "experimenter-rapid/contexts/experiment/actions";
import {
useExperimentDispatch,
useExperimentState,
} from "experimenter-rapid/contexts/experiment/hooks";
import { ExperimentReducerActionType } from "experimenter-types/experiment";

import { featureOptions, audienceOptions } from "./ExperimentFormOptions";
import { XSelect } from "./XSelect";
Expand Down Expand Up @@ -40,38 +43,18 @@ const ExperimentForm: React.FC = () => {

const handleSelectChange = (name) => {
return (value) => {
dispatch({
type: ExperimentReducerActionType.UPDATE_STATE,
state: {
...formData,
[name]: value,
},
});
dispatch(updateExperiment(name, value));
};
};

const handleChange = (ev) => {
const field = ev.target;
dispatch({
type: ExperimentReducerActionType.UPDATE_STATE,
state: {
...formData,
[field.getAttribute("name")]: field.value,
},
});
dispatch(updateExperiment(field.getAttribute("name"), field.value));
};

const handleClickSave = async () => {
const url = experimentSlug
? `/api/v3/experiments/${experimentSlug}/`
: "/api/v3/experiments/";
const response = await fetch(url, {
method: experimentSlug ? "PUT" : "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
});
const response = await saveExperiment(experimentSlug, formData);

const responseData = await response.json();
if (!response.ok) {
setErrors(responseData);
Expand Down
52 changes: 52 additions & 0 deletions app/experimenter/static/rapid/contexts/experiment/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
ExperimentReducerActionType,
ExperimentData,
} from "experimenter-types/experiment";
import { ExperimentReducerAction } from "experimenter-types/experiment";

export const fetchExperiment = (experimentSlug: string) => async (
experimentData: ExperimentData,
dispatch: React.Dispatch<ExperimentReducerAction>,
): Promise<void> => {
const response = await fetch(`/api/v3/experiments/${experimentSlug}/`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});

const data = await response.json();
dispatch({
type: ExperimentReducerActionType.UPDATE_STATE,
state: data,
});
};

export const saveExperiment = async (
experimentSlug: string,
formData: ExperimentData,
): Promise<Response> => {
const url = experimentSlug
? `/api/v3/experiments/${experimentSlug}/`
: "/api/v3/experiments/";
return await fetch(url, {
method: experimentSlug ? "PUT" : "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
});
};

export const updateExperiment = (name: string, value: string | string[]) => (
experimentData: ExperimentData,
dispatch: React.Dispatch<ExperimentReducerAction>,
): void => {
dispatch({
type: ExperimentReducerActionType.UPDATE_STATE,
state: {
...experimentData,
[name]: value,
},
});
};
9 changes: 6 additions & 3 deletions app/experimenter/static/rapid/contexts/experiment/context.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import React from "react";

import { ExperimentContext } from "experimenter-types/experiment";
import { Action, ExperimentData } from "experimenter-types/experiment";

export const INITIAL_CONTEXT: ExperimentContext = {
export const INITIAL_CONTEXT: {
state: ExperimentData;
dispatch: (action: Action) => void;
} = {
state: {
name: "",
objectives: "",
features: [],
audience: "",
},
dispatch(action) {
dispatch: (action: Action) => {
/* istanbul ignore next */
console.log(action);
},
Expand Down
9 changes: 2 additions & 7 deletions app/experimenter/static/rapid/contexts/experiment/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import React from "react";

import context from "experimenter-rapid/contexts/experiment/context";
import {
ExperimentData,
ExperimentReducerAction,
} from "experimenter-types/experiment";
import { Dispatch, ExperimentData } from "experimenter-types/experiment";

export function useExperimentState(): ExperimentData {
const { state } = React.useContext(context);
return state;
}

export function useExperimentDispatch(): React.Dispatch<
ExperimentReducerAction
> {
export function useExperimentDispatch(): Dispatch {
const { dispatch } = React.useContext(context);
return dispatch;
}
26 changes: 5 additions & 21 deletions app/experimenter/static/rapid/contexts/experiment/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,28 @@
import React from "react";
import { useParams } from "react-router";

import { fetchExperiment } from "experimenter-rapid/contexts/experiment/actions";
import context, {
INITIAL_CONTEXT,
} from "experimenter-rapid/contexts/experiment/context";
import reducer from "experimenter-rapid/contexts/experiment/reducer";
import {
ExperimentData,
ExperimentReducerActionType,
} from "experimenter-types/experiment";
import { Action, ExperimentData } from "experimenter-types/experiment";

const ExperimentProvider: React.FC<{ initialState?: ExperimentData }> = ({
children,
initialState = INITIAL_CONTEXT.state,
}) => {
const [state, dispatch] = React.useReducer(reducer, initialState);
const [state, reducerDispatch] = React.useReducer(reducer, initialState);
const { experimentSlug } = useParams();
const { Provider } = context;

const dispatch = (action: Action) => action(state, reducerDispatch);
React.useEffect(() => {
if (!experimentSlug) {
return;
}

const fetchData = async () => {
const response = await fetch(`/api/v3/experiments/${experimentSlug}/`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});

const data = await response.json();
dispatch({
type: ExperimentReducerActionType.UPDATE_STATE,
state: data,
});
};

fetchData();
dispatch(fetchExperiment(experimentSlug));
}, [experimentSlug]);

return <Provider value={{ state, dispatch }}>{children}</Provider>;
Expand Down
10 changes: 6 additions & 4 deletions app/experimenter/static/rapid/types/experiment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export interface ExperimentReducerAction {
state: ExperimentData;
}

export interface ExperimentContext {
state: ExperimentData;
dispatch: React.Dispatch<ExperimentReducerAction>;
}
export type Action = (
experimentData: ExperimentData,
dispatch: React.Dispatch<ExperimentReducerAction>,
) => void;

export type Dispatch = (action: Action) => void;

0 comments on commit 29d1431

Please sign in to comment.