Шаблоны: правильный способ реализации меню конфигурации в React - PullRequest
0 голосов
/ 14 апреля 2020

Есть одна вещь, которую я не понимаю, как правильно делать в чистом React (без Redux и Co по многим причинам, которые я не хочу здесь объяснять), и мы можем упростить проблему, изучив фиктивный флажок, который мы масштабируем до целого меню конфигурации.

Я вижу 3 способа достижения этого, и все, кажется, имеют недостатки, когда вы хотите увеличить масштаб. Давайте посмотрим.

Флажок без состояния

const Checkbox = ({
  onPress = () => {},
  checked = false
}) => {
  return <input type="checkbox" checked={checked} />
}

Теперь, если у меня есть полное меню конфигурации с несколькими флажками, это означает, что родитель должен иметь состояние, содержащее все состояния всех флажков, и предоставить все как реквизиты в меню. См .:

const Parent = () => {
  const [isBox1Checked, checkBox1] = useState(false)
  const [isBox2Checked, checkBox2] = useState(false)
  const [isBox3Checked, checkBox3] = useState(false)
  const [isBox4Checked, checkBox4] = useState(false)
  const [isBox5Checked, checkBox5] = useState(false)
  return <Menu isBox1Checked={isBox1Checked} checkBox1={checkBox1} ... />
}

Я мог бы использовать массив, но имейте в виду, что то, что здесь упрощено, поскольку флажки будут всеми видами параметров (числа, логические значения, строки и т. Д. c)

Меню будет выглядеть так:

const Menu = ({isBox1Checked, checkBox1, ... }) => {
  return <div>
    <Checkbox onPress={checkBox1} checked={isBox1Checked} />
    <Checkbox onPress={checkBox2} checked={isBox2Checked} />
    <Checkbox onPress={checkBox3} checked={isBox3Checked} />
    <Checkbox onPress={checkBox4} checked={isBox4Checked} />
    <Checkbox onPress={checkBox5} checked={isBox5Checked} />
  </div>
}

Плюсы:

  • Легко читать текущую конфигурацию, где это необходимо

Минусы:

  • Множество глупых кодов, которые нужно написать, просто чтобы передать все реквизиты
  • Техническое обслуживание и рефакто, вероятно, станут кошмаром .. .

Флажок с отслеживанием состояния

const Checkbox = ({
  onChange = () => {},
  initialValue = false
}) => {
  const [checked, setChecked] = useState(initialValue)
  const toggle = () => {
    setChecked(!checked)
    onChange(!checked)
  }
  return <input type="checkbox" checked={checked} onChange={toggle}/>
}

Здесь мы сохраняем состояние флажка внутри флажка, который становится владельцем правды. Родственники могут узнать об изменениях состояния с помощью метода onChange.

К сожалению, компонент Parent и Menu будет выглядеть точно так же, как и предыдущие ...

Плюсы:

  • Больше Компонент-ориентированный флажок, который лучше подходит для разделения проблем.

Минусы:

  • A много глупого кода, который нужно написать, просто чтобы передать все реквизиты
  • Обслуживание и рефакто, вероятно, станут кошмаром ...
  • Дублирование данных, так как есть флажок внутри флажка Флажок и состояние для функциональности в Parent ...
  • Может возникнуть путаница в предоставлении initialValue в качестве реквизита, который будет игнорироваться при обновлении, поскольку реальность находится в состоянии

Чтение состояния с помощью ссылок

const Checkbox = forwardRef(({
  onChange = () => {},
  initialValue = false
}, ref) => {
  const [checked, setChecked] = useState(initialValue)
  //We wait for the DOM to be rendered for ref.current to worth something.
  useLayoutEffect(() => ref.current.isChecked = () => checked, [ref.current])
  const toggle = () => {
    setChecked(!checked)
    onChange(!checked)
  }
  return <input ref={ref} type="checkbox" checked={checked} onChange={toggle}/>
})

На этот раз флажок становится истинным владельцем правды, и для чтения данных мы читаем их непосредственно внутри флажка. Родитель стал бы чем-то вроде этого:

const Parent = () => {
  const menu = createRef()
  const onConfigChanged = ({ name, value }) => //Do sth if sth changed
  const isBox1Checked = menu.current ? menu.current.isChecked('box1') : false
  //Do something with valueOfParam1
  return <Menu ref={menu} onChange={onConfigChanged} />
}

const Menu = forwardRef({ onConfigChanged }, ref) => {
  // We create the refs for all the configs that this menu will be in charge for
  const configs = useRef({
    box1: createRef(),
    box2: createRef(),
    box3: createRef(),
    box4: createRef(),
    box5: createRef(),
  })
  // We provide a method to read the state of each checkbox by accessing their ref.
  if(ref.current) ref.current.isChecked = name => configs[name].current.isChecked
  const onChanged = name => value => onConfigChanged({name, value})
  return <div ref={ref}>
    <Checkbox ref={configs.current.box1} onChange={onConfigChanged('box1')} />
    <Checkbox ref={configs.current.box2} onChange={onConfigChanged('box2')} />
    <Checkbox ref={configs.current.box3} onChange={onConfigChanged('box3')} />
    <Checkbox ref={configs.current.box4} onChange={onConfigChanged('box4')} />
    <Checkbox ref={configs.current.box5} onChange={onConfigChanged('box5')} />
  </div>
})

Плюсы:

  • 100% Ориентация на компоненты с разделением интересов
  • Родитель Код упрощен, что помогает подчеркнуть их собственную ответственность, а не наводнять ее распределением состояний

Минусы:

  • Предположительно анти-паттерн использования ссылок для доступа к дочерним методам
  • Может возникнуть путаница в предоставлении initialValue в качестве реквизита, который будет игнорироваться при обновлении, поскольку реальность находится в состоянии
  • Текущая реализация Parent имеет недостаток так как он не рендерится при изменении конфигурации.

Так, как правильно это реализовать?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...