From 204a4c02d16ffad189a6e8a6001d5bf4ff95fc4e Mon Sep 17 00:00:00 2001 From: f0x Date: Mon, 30 Jan 2023 20:05:34 +0100 Subject: [PATCH] checklist field: use reducer for state --- web/source/index.js | 2 +- web/source/settings/lib/form/check-list.jsx | 155 +++++++++++--------- 2 files changed, 84 insertions(+), 73 deletions(-) diff --git a/web/source/index.js b/web/source/index.js index 0f80ef22c..53c265fa4 100644 --- a/web/source/index.js +++ b/web/source/index.js @@ -66,7 +66,7 @@ skulk({ ], }, settings: { - debug: false, + debug: true, entryFile: "settings", outputFile: "settings.js", prodCfg: prodCfg, diff --git a/web/source/settings/lib/form/check-list.jsx b/web/source/settings/lib/form/check-list.jsx index c1233273d..38291ee06 100644 --- a/web/source/settings/lib/form/check-list.jsx +++ b/web/source/settings/lib/form/check-list.jsx @@ -20,47 +20,50 @@ const React = require("react"); const syncpipe = require("syncpipe"); +const { createSlice } = require("@reduxjs/toolkit"); -function createState(entries, uniqueKey, oldState, defaultValue) { - return syncpipe(entries, [ - (_) => _.map((entry) => { - let key = entry[uniqueKey]; - return [ - key, - { +const { reducer, actions } = createSlice({ + name: "checklist", + initialState: {}, + reducers: { + create: (state, { payload }) => { + const { entries, uniqueKey, defaultValue } = payload; + return syncpipe(entries, [ + (_) => _.map((entry) => { + let key = entry[uniqueKey]; + return [ + key, + { + ...entry, + key, + checked: state[key]?.checked ?? entry.checked ?? defaultValue + } + ]; + }), + (_) => Object.fromEntries(_) + ]); + }, + updateAll: (state, { payload: value }) => { + return syncpipe(state, [ + (_) => Object.values(_), + (_) => _.map((entry) => [entry.key, { ...entry, - key, - checked: oldState[key]?.checked ?? entry.checked ?? defaultValue - } - ]; - }), - (_) => Object.fromEntries(_) - ]); -} - -function updateAllState(state, newValue) { - return syncpipe(state, [ - (_) => Object.values(_), - (_) => _.map((entry) => [entry.key, { - ...entry, - checked: newValue - }]), - (_) => Object.fromEntries(_) - ]); -} - -function updateState(state, key, newValue) { - return { - ...state, - [key]: { - ...state[key], - ...newValue + checked: value + }]), + (_) => Object.fromEntries(_) + ]); + }, + update: (state, { payload: { key, value } }) => { + state[key] = { + ...state[key], + ...value + }; } - }; -} + } +}); module.exports = function useCheckListInput({ name }, { entries, uniqueKey = "key", defaultValue = false }) { - const [state, setState] = React.useState({}); + const [state, dispatch] = React.useReducer(reducer, {}); const [someSelected, setSomeSelected] = React.useState(false); const [toggleAllState, setToggleAllState] = React.useState(0); @@ -71,11 +74,13 @@ module.exports = function useCheckListInput({ name }, { entries, uniqueKey = "ke entries changed, update state, re-using old state if available for key */ - setState(createState(entries, uniqueKey, state, defaultValue)); + dispatch(actions.create({ entries, uniqueKey, defaultValue })); /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [entries]); + console.log(state); + React.useEffect(() => { /* Updates (un)check all checkbox, based on shortcode checkboxes Can be 0 (not checked), 1 (checked) or 2 (indeterminate) @@ -105,43 +110,49 @@ module.exports = function useCheckListInput({ name }, { entries, uniqueKey = "ke } }, [state, toggleAllRef]); - function toggleAll(e) { - let selectAll = e.target.checked; + return React.useMemo(() => { + function toggleAll(e) { + let selectAll = e.target.checked; - if (toggleAllState == 2) { // indeterminate - selectAll = false; + if (toggleAllState == 2) { // indeterminate + selectAll = false; + } + + dispatch(actions.updateAll(selectAll)); + setToggleAllState(selectAll); } - setState(updateAllState(state, selectAll)); - setToggleAllState(selectAll); - } - - function reset() { - setState(updateAllState(state, defaultValue)); - } - - function selectedValues() { - return syncpipe(state, [ - (_) => Object.values(_), - (_) => _.filter((entry) => entry.checked) - ]); - } - - return Object.assign([ - state, - reset, - { name } - ], { - name, - value: state, - onChange: (key, newValue) => setState(updateState(state, key, newValue)), - selectedValues, - reset, - someSelected, - toggleAll: { - ref: toggleAllRef, - value: toggleAllState, - onChange: toggleAll + function reset() { + dispatch(actions.updateAll(defaultValue)); } - }); + + function onChange(key, value) { + dispatch(actions.update({ key, value })); + } + + function selectedValues() { + return syncpipe(state, [ + (_) => Object.values(_), + (_) => _.filter((entry) => entry.checked) + ]); + } + + return Object.assign([ + state, + reset, + { name } + ], { + name, + value: state, + onChange, + selectedValues, + reset, + someSelected, + toggleAll: { + ref: toggleAllRef, + value: toggleAllState, + onChange: toggleAll + } + }); + }, [defaultValue, name, someSelected, state, toggleAllState]); }; \ No newline at end of file