refactor(ui): simplify notification rule state management
parent
fcfa827e8e
commit
672f6eca93
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
}
|
Loading…
Reference in New Issue