grafana/contribute/style-guides/redux.md
Joseph Perez 8cc95a7459
Docs: Edit contribute/style-guides (part 11 of doc improvement project) (#92169)
* Docs: edit contribute/style-guides

* Add missing 'is'

* Improve grammar of code comment

* Prettier fixes

* Minor fix

* Minor fix

---------

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
2024-08-23 08:08:04 -07:00

2.7 KiB

Redux framework

Grafana uses Redux Toolkit to handle Redux boilerplate code.

Note: Some of our reducers are used by Angular; therefore, consider state to be mutable for those reducers.

Test functionality

Here's how to test the functioning of your Redux reducers.

reducerTester

Use the Fluent API framework to simplify the testing of reducers.

Usage

Example of reducerTester in use:

reducerTester()
  .givenReducer(someReducer, initialState)
  .whenActionIsDispatched(someAction('reducer tests'))
  .thenStateShouldEqual({ ...initialState, data: 'reducer tests' });

Complex usage

Sometimes you encounter a resulting state that contains properties that are hard to compare, such as Dates, but you still want to evaluate whether other props in state are correct.

In these cases, you can evaluate individual properties by using thenStatePredicateShouldEqual function on reducerTester that will return the resulting state. For example:

reducerTester()
  .givenReducer(someReducer, initialState)
  .whenActionIsDispatched(someAction('reducer tests'))
  .thenStatePredicateShouldEqual((resultingState) => {
    expect(resultingState.data).toEqual('reducer tests');
    return true;
  });

thunkTester

Here's a Fluent API function that simplifies the testing of thunks.

Usage

Example of thunkTester in use:

const dispatchedActions = await thunkTester(initialState).givenThunk(someThunk).whenThunkIsDispatched(arg1, arg2, arg3);

expect(dispatchedActions).toEqual([someAction('reducer tests')]);

Typing of connected props

It is possible to infer connected props automatically from mapStateToProps and mapDispatchToProps using a helper type ConnectedProps from Redux. For this to work properly, split the connect call into two parts like so:

import { connect, ConnectedProps } from 'react-redux';

const mapStateToProps = (state: StoreState) => {
  return {
    location: state.location,
    initDone: state.panelEditor.initDone,
    uiState: state.panelEditor.ui,
  };
};

const mapDispatchToProps = {
  updateLocation,
  initPanelEditor,
  panelEditorCleanUp,
  setDiscardChanges,
  updatePanelEditorUIState,
  updateTimeZoneForSession,
};

const connector = connect(mapStateToProps, mapDispatchToProps);

type Props = OwnProps & ConnectedProps<typeof connector>;

class PanelEditorUnconnected extends PureComponent<Props> {}

export const PanelEditor = connector(PanelEditorUnconnected);

For more examples, refer to the Redux documentation.