refactor(ui): simplify notification rule state management

pull/14663/head
Christopher Henn 2019-08-14 11:39:17 -07:00
parent fcfa827e8e
commit 672f6eca93
12 changed files with 108 additions and 138 deletions

View File

@ -7,11 +7,8 @@ import {connect} from 'react-redux'
import {Overlay} from '@influxdata/clockface'
import RuleOverlayContents from 'src/alerting/components/notifications/RuleOverlayContents'
// Reducers
import {memoizedReducer, ActionPayload} from './RuleOverlay.reducer'
// Constants
import {RuleModeContext, EditRuleDispatch, RuleMode} from 'src/shared/hooks'
// Utils
import {RuleOverlayProvider} from './RuleOverlay.reducer'
// Types
import {NotificationRuleDraft, AppState} from 'src/types'
@ -31,28 +28,20 @@ const EditRuleOverlay: FC<Props> = ({params, router, stateRule}) => {
router.push(`/orgs/${params.orgID}/alerting`)
}
const mode = RuleMode.Edit
const [rule, dispatch] = memoizedReducer(mode, stateRule)
const ruleDispatch = (action: ActionPayload): void => {
dispatch({...action, mode})
}
return (
<RuleModeContext.Provider value={mode}>
<EditRuleDispatch.Provider value={ruleDispatch}>
<Overlay visible={true}>
<Overlay.Container maxWidth={800}>
<Overlay.Header
title="Edit this Notification Rule"
onDismiss={handleDismiss}
/>
<Overlay.Body>
<RuleOverlayContents rule={rule} />
</Overlay.Body>
</Overlay.Container>
</Overlay>
</EditRuleDispatch.Provider>
</RuleModeContext.Provider>
<RuleOverlayProvider initialState={stateRule}>
<Overlay visible={true}>
<Overlay.Container maxWidth={800}>
<Overlay.Header
title="Edit this Notification Rule"
onDismiss={handleDismiss}
/>
<Overlay.Body>
<RuleOverlayContents />
</Overlay.Body>
</Overlay.Container>
</Overlay>
</RuleOverlayProvider>
)
}

View File

@ -1,20 +1,17 @@
// Libraries
import React, {FC, useReducer, useCallback} from 'react'
import React, {FC} from 'react'
import {withRouter, WithRouterProps} from 'react-router'
// Components
import RuleOverlayContents from 'src/alerting/components/notifications/RuleOverlayContents'
import {Overlay} from '@influxdata/clockface'
// Reducers
import {reducer, ActionPayload} from './RuleOverlay.reducer'
// Utils
import {RuleOverlayProvider} from './RuleOverlay.reducer'
// Constants
import {newRule} from 'src/alerting/constants'
// Context
import {RuleMode, NewRuleDispatch, RuleModeContext} from 'src/shared/hooks'
type Props = WithRouterProps
const NewRuleOverlay: FC<Props> = ({params, router}) => {
@ -22,30 +19,20 @@ const NewRuleOverlay: FC<Props> = ({params, router}) => {
router.push(`/orgs/${params.orgID}/alerting`)
}
const mode = RuleMode.New
const memoizedReducer = useCallback(reducer(mode), [])
const [rule, dispatch] = useReducer(memoizedReducer, newRule)
const ruleDispatch = (action: ActionPayload) => {
dispatch({...action, mode})
}
return (
<RuleModeContext.Provider value={RuleMode.New}>
<NewRuleDispatch.Provider value={ruleDispatch}>
<Overlay visible={true}>
<Overlay.Container maxWidth={800}>
<Overlay.Header
title="Create a Notification Rule"
onDismiss={handleDismiss}
/>
<Overlay.Body>
<RuleOverlayContents rule={rule} />
</Overlay.Body>
</Overlay.Container>
</Overlay>
</NewRuleDispatch.Provider>
</RuleModeContext.Provider>
<RuleOverlayProvider initialState={newRule}>
<Overlay visible={true}>
<Overlay.Container maxWidth={800}>
<Overlay.Header
title="Create a Notification Rule"
onDismiss={handleDismiss}
/>
<Overlay.Body>
<RuleOverlayContents />
</Overlay.Body>
</Overlay.Container>
</Overlay>
</RuleOverlayProvider>
)
}

View File

@ -15,15 +15,15 @@ import StatusRuleComponent from 'src/alerting/components/notifications/StatusRul
import TagRuleComponent from 'src/alerting/components/notifications/TagRule'
import DashedButton from 'src/shared/components/dashed_button/DashedButton'
// Utils
import {useRuleDispatch} from './RuleOverlay.reducer'
// Constants
import {newTagRule} from 'src/alerting/constants'
// Types
import {RuleState} from './RuleOverlay.reducer'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
interface Props {
rule: RuleState
}

View File

@ -6,14 +6,12 @@ import {Form, Panel, Grid, Columns} from '@influxdata/clockface'
import RuleEndpointDropdown from 'src/alerting/components/notifications/RuleEndpointDropdown'
import RuleMessageContents from 'src/alerting/components/notifications/RuleMessageContents'
// Types
import {NotificationEndpoint, NotificationRuleDraft} from 'src/types'
// Utils
import {getEndpointBase} from 'src/alerting/components/notifications/utils'
import {useRuleDispatch} from './RuleOverlay.reducer'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
// Types
import {NotificationEndpoint, NotificationRuleDraft} from 'src/types'
interface Props {
endpoints: NotificationEndpoint[]

View File

@ -6,12 +6,12 @@ import SlackMessage from './SlackMessage'
import SMTPMessage from './SMTPMessage'
import PagerDutyMessage from './PagerDutyMessage'
// Utils
import {useRuleDispatch} from './RuleOverlay.reducer'
// Types
import {NotificationRuleDraft} from 'src/types'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
interface Props {
rule: NotificationRuleDraft
}

View File

@ -1,5 +1,12 @@
// Libraries
import {useCallback, useReducer} from 'react'
import React, {
createContext,
useContext,
useReducer,
useRef,
Dispatch,
FC,
} from 'react'
import {v4} from 'uuid'
import {omit} from 'lodash'
@ -11,13 +18,11 @@ import {
CheckStatusLevel,
} from 'src/types'
// Hooks
import {RuleMode} from 'src/shared/hooks'
export type LevelType = 'currentLevel' | 'previousLevel'
export type RuleState = NotificationRuleDraft
export type ActionMode = {mode: RuleMode}
export type ActionPayload =
export type Action =
| {type: 'UPDATE_RULE'; rule: NotificationRuleDraft}
| {
type: 'UPDATE_STATUS_LEVEL'
@ -37,16 +42,7 @@ export type ActionPayload =
operator: TagRuleDraft['value']['operator']
}
export type Action = ActionPayload & ActionMode
export const reducer = (mode: RuleMode) => (
state: RuleState,
action: Action
) => {
if (mode !== action.mode) {
return state
}
const reducer = (state: RuleState, action: Action) => {
switch (action.type) {
case 'UPDATE_RULE': {
const {rule} = action
@ -167,13 +163,47 @@ export const reducer = (mode: RuleMode) => (
default:
const neverAction: never = action
throw new Error(
`Unhandled action: "${neverAction}" in RuleOverlay.reducer.ts`
`Unhandled action "${
(neverAction as any).type
}" in RuleOverlay.reducer.ts`
)
}
}
export const memoizedReducer = (mode: RuleMode, state) => {
const memo = useCallback(reducer(mode), [mode])
return useReducer(memo, state)
const RuleStateContext = createContext<RuleState>(null)
const RuleDispatchContext = createContext<Dispatch<Action>>(null)
export const RuleOverlayProvider: FC<{initialState: RuleState}> = ({
initialState,
children,
}) => {
const prevInitialStateRef = useRef(initialState)
const [state, dispatch] = useReducer((state: RuleState, action: Action) => {
if (prevInitialStateRef.current !== initialState) {
prevInitialStateRef.current = initialState
return initialState
}
return reducer(state, action)
}, initialState)
return (
<RuleStateContext.Provider value={state}>
<RuleDispatchContext.Provider value={dispatch}>
{children}
</RuleDispatchContext.Provider>
</RuleStateContext.Provider>
)
}
export const useRuleState = (): RuleState => {
return useContext(RuleStateContext)
}
export const useRuleDispatch = (): Dispatch<Action> => {
return useContext(RuleDispatchContext)
}

View File

@ -14,23 +14,22 @@ import RuleSchedule from 'src/alerting/components/notifications/RuleSchedule'
import RuleConditions from 'src/alerting/components/notifications/RuleConditions'
import RuleMessage from 'src/alerting/components/notifications/RuleMessage'
// Types
import {NotificationRuleDraft} from 'src/types'
// Constants
import {endpoints} from 'src/alerting/constants'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
// Utils
import {useRuleState, useRuleDispatch} from './RuleOverlay.reducer'
interface Props {
rule: NotificationRuleDraft
}
// Types
import {NotificationRuleDraft} from 'src/types'
const RuleOverlayContents: FC<Props> = ({rule}) => {
const RuleOverlayContents: FC = () => {
const rule = useRuleState()
const dispatch = useRuleDispatch()
const handleChange = e => {
const {name, value} = e.target
dispatch({
type: 'UPDATE_RULE',
rule: {...rule, [name]: value} as NotificationRuleDraft,

View File

@ -12,8 +12,8 @@ import {
ButtonShape,
} from '@influxdata/clockface'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
// Utils
import {useRuleDispatch} from './RuleOverlay.reducer'
// Types
import {RuleState} from './RuleOverlay.reducer'

View File

@ -8,15 +8,13 @@ import {StatusRuleDraft} from 'src/types'
import {Dropdown} from '@influxdata/clockface'
// Utils
import {useRuleDispatch} from './RuleOverlay.reducer'
import {
CHANGES,
changeStatusRule,
activeChange,
} from 'src/alerting/components/notifications/utils'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
interface Props {
status: StatusRuleDraft
}

View File

@ -12,12 +12,12 @@ import LevelsDropdown from 'src/alerting/components/notifications/LevelsDropdown
import StatusChangeDropdown from 'src/alerting/components/notifications/StatusChangeDropdown'
import {LevelType} from 'src/alerting/components/notifications/RuleOverlay.reducer'
// Utils
import {useRuleDispatch} from './RuleOverlay.reducer'
// Types
import {StatusRuleDraft, CheckStatusLevel} from 'src/types'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
interface Props {
status: StatusRuleDraft
}

View File

@ -12,16 +12,17 @@ import {
FlexDirection,
ComponentColor,
} from '@influxdata/clockface'
import TagRuleOperatorDropdown, {
Operator,
} from 'src/alerting/components/notifications/TagRuleOperatorDropdown'
// Utils
import {useRuleDispatch} from './RuleOverlay.reducer'
// Types
import {TagRuleDraft} from 'src/types'
// Hooks
import {useRuleDispatch} from 'src/shared/hooks'
interface Props {
tagRule: TagRuleDraft
}

View File

@ -1,32 +0,0 @@
import React, {useContext} from 'react'
import {ActionPayload} from 'src/alerting/components/notifications/RuleOverlay.reducer'
export enum RuleMode {
New = 'NewRuleDispatch',
Edit = 'EditRuleDispatch',
}
export const RuleModeContext = React.createContext<RuleMode>(null)
export const NewRuleDispatch = React.createContext<
(action: ActionPayload) => void
>(null)
export const EditRuleDispatch = React.createContext<
(action: ActionPayload) => void
>(null)
interface Contexts {
NewRuleDispatch: typeof NewRuleDispatch
EditRuleDispatch: typeof EditRuleDispatch
}
export const contexts: Contexts = {
NewRuleDispatch,
EditRuleDispatch,
}
export const useRuleDispatch = () => {
const mode = useContext(RuleModeContext)
return useContext(contexts[mode])
}