Как разрешить только одно активное состояние useState

Мне нужно создать логику заказа. У меня есть множество контактов, и мне нужно их заказать. Итак, я создаю модальное окно с 4 вариантами и 4 состояниями использования, я хочу создать такое условие, как:

  contactsArr.sort(function (a, b) {
        if(isRecent === true){
          return (a.lastMessage.sentAt > b.lastMessage.sentAt) ? -1 : ((a.lastMessage.sentAt < b.lastMessage.sentAt) ? 1 : 0);
        }
        
        if(isOld === true){
          return (b.lastMessage.sentAt > a.lastMessage.sentAt) ? -1 : ((b.lastMessage.sentAt < a.lastMessage.sentAt) ? 1 : 0);
        }
    ...
}

мой компонент orderModal:

import React, { useState } from 'react';

import { Container, Overlay, Column, Row, Text, Button, Wrapper, Icon } from './styles';

const ModalOrder = ({modalIsOpen, setModalIsOpen}) => {
  
  //change states names
  const [isSelected1, setIsSelected1] = useState(false);
  const [isSelected2, setIsSelected2] = useState(false);
  const [isSelected3, setIsSelected3] = useState(false);
  const [isSelected4, setIsSelected4] = useState(false);
  const [isSelected5, setIsSelected5] = useState(false);

  return (
    <>  
    <Overlay display={modalIsOpen ? "flex" : "none"} onClick={() => setModalIsOpen(s => !s)} >
    </Overlay>
      <Wrapper height={modalIsOpen} shadow="0px -4px 4px rgba(0, 0, 0, 0.1);" display={"flex"} margin="10px 0 0 0" align="center" justify="center" zIndex="2011">
        {/* <Row transform={modalIsOpen ? "translateY(0)" : "translateY(327px)"} cursor="pointer" onClick={() => setModalIsOpen(s => !s)} custom="bottom: 0; position: absolute; margin-bottom: 320px; transition: all ease 0.3s;" width="90px" height="6px" background="#FFF9F9" radius="100px" shadow="0px 4px 4px rgba(0, 0, 0, 0.1);"></Row> */}
        <Icon custom="bottom: 0; position: absolute; margin-bottom: 310px; transition: all ease 0.3s;" transform={modalIsOpen ? "translateY(0)" : "translateY(360px)!important"} onClick={() => setModalIsOpen(s => !s)}/>
        <Column transform={modalIsOpen ? "translateY(0)" : "translateY(309px)"} align="center"  custom="bottom: 0; position: absolute; border-top-left-radius: 35px; border-top-right-radius: 35px; transition: all ease 0.3s;" width="100%" height="308px" background="#FFF" position="absolute">
          <Text margin="16px 0 12px 0" font="bold 16px/21px Segoe UI Regular" color="#448757" >Order</Text>
          
          <Button borderT="1px solid rgb(196, 196, 196, 0.8)" borderB="1px solid rgb(196, 196, 196, 0.8)" onClick={() => setIsSelected1(s => !s)} height="32px" width="100%" align="center" justify="start"> 
            <Row shadow="2px 0px 4px rgba(0, 0, 0, 0.1);" display={isSelected1 ? "flex" : "none"}  custom="border-bottom-right-radius: 5px; border-top-right-radius: 5px; position: absolute; left: 0;" width="10px" height="32px" background="#448757" /> 
            <Text margin="0 0 0 20px" font="normal 14px/19px Segoe UI Regular" color="#525252" >Leatest</Text> 
          </Button>
         
          <Button borderB="1px solid rgb(196, 196, 196, 0.8)" onClick={() => setIsSelected2(s => !s)} height="32px" width="100%" align="center" justify="start">
            <Row shadow="2px 0px 4px rgba(0, 0, 0, 0.1);" display={isSelected2 ? "flex" : "none"}  custom="border-bottom-right-radius: 5px; border-top-right-radius: 5px; position: absolute; left: 0;" width="10px" height="32px" background="#448757" />  
            <Text margin="0 0 0 20px" font="normal 14px/19px Segoe UI Regular" color="#525252" >Oldest</Text> 
          </Button>
         
          <Button borderB="1px solid rgb(196, 196, 196, 0.8)" onClick={() => setIsSelected3(s => !s)} height="32px" width="100%" align="center" justify="start">
            <Row shadow="2px 0px 4px rgba(0, 0, 0, 0.1);" display={isSelected3 ? "flex" : "none"}  custom="border-bottom-right-radius: 5px; border-top-right-radius: 5px; position: absolute; left: 0;" width="10px" height="32px" background="#448757" />  
            <Text margin="0 0 0 20px" font="normal 14px/19px Segoe UI Regular" color="#525252" >Read</Text> 
          </Button>
          
          <Button borderB="1px solid rgb(196, 196, 196, 0.8)" onClick={() => setIsSelected4(s => !s)} height="32px" width="100%" align="center" justify="start"> 
            <Row shadow="2px 0px 4px rgba(0, 0, 0, 0.1);" display={isSelected4 ? "flex" : "none"}  custom="border-bottom-right-radius: 5px; border-top-right-radius: 5px; position: absolute; left: 0;" width="10px" height="32px" background="#448757" /> 
            <Text margin="0 0 0 20px" font="normal 14px/19px Segoe UI Regular" color="#525252" >Unread</Text> 
          </Button>
 
        </Column>
      </Wrapper>
    </>
  );
};

export default ModalOrder;

Что я могу сделать, чтобы сохранить истинным только одно значение useState? (активно) Без необходимости создавать 4 разных константы, которые устанавливают для выбранного значения TRUE, а для других - False.

My UI

введите описание изображения здесь


person luck    schedule 26.01.2021    source источник


Ответы (3)


вы можете сначала создать компонент CustomButton, чтобы ваш код оставался сухим:

const CustomButton = ({selected, setSelected, children}) => (     
  <Button borderT="1px solid rgb(196, 196, 196, 0.8)" borderB="1px solid rgb(196, 196, 196, 0.8)" onClick={setSelected} height="32px" width="100%" align="center" justify="start"> 
    <Row shadow="2px 0px 4px rgba(0, 0, 0, 0.1);" display={selected ? "flex" : "none"}  custom="border-bottom-right-radius: 5px; border-top-right-radius: 5px; position: absolute; left: 0;" width="10px" height="32px" background="#448757" /> 
    <Text margin="0 0 0 20px" font="normal 14px/19px Segoe UI Regular" color="#525252" >{children}</Text> 
  </Button>
)

затем создайте состояние с начальным значением null. Создайте buttons переменную с ее содержимым. В этом примере вы можете использовать контент для установки своего selected состояния и сравнения (поскольку все они уникальны), но часто лучше иметь уникальный ключ к нему.

Затем вы проходите через свой buttons, где вы передаете selected и setSelected, как показано ниже:

const ModalOrder = ({modalIsOpen, setModalIsOpen}) => {
  
  //change states names
  const [selected, setSelected] = useState(null);
  const buttons = ['Leatest', 'Oldest', 'Read', 'Unread'];

  return (
    <>  
    <Overlay display={modalIsOpen ? "flex" : "none"} onClick={() => setModalIsOpen(s => !s)} >
    </Overlay>
      <Wrapper height={modalIsOpen} shadow="0px -4px 4px rgba(0, 0, 0, 0.1);" display={"flex"} margin="10px 0 0 0" align="center" justify="center" zIndex="2011">
        {/* <Row transform={modalIsOpen ? "translateY(0)" : "translateY(327px)"} cursor="pointer" onClick={() => setModalIsOpen(s => !s)} custom="bottom: 0; position: absolute; margin-bottom: 320px; transition: all ease 0.3s;" width="90px" height="6px" background="#FFF9F9" radius="100px" shadow="0px 4px 4px rgba(0, 0, 0, 0.1);"></Row> */}
        <Icon custom="bottom: 0; position: absolute; margin-bottom: 310px; transition: all ease 0.3s;" transform={modalIsOpen ? "translateY(0)" : "translateY(360px)!important"} onClick={() => setModalIsOpen(s => !s)}/>
        <Column transform={modalIsOpen ? "translateY(0)" : "translateY(309px)"} align="center"  custom="bottom: 0; position: absolute; border-top-left-radius: 35px; border-top-right-radius: 35px; transition: all ease 0.3s;" width="100%" height="308px" background="#FFF" position="absolute">
          <Text margin="16px 0 12px 0" font="bold 16px/21px Segoe UI Regular" color="#448757" >Order</Text>
          
          { 
            buttons.map(content => (
              <CustomButton key={content} 
                      selected={selected === content}
                      setSelected={() => setSelected(content)}>{content}
              </CustomButton>
            ))
          }
 
        </Column>
      </Wrapper>
    </>
  );
};

export default ModalOrder;
person buzatto    schedule 26.01.2021
comment
Вот простой пример (без пушистого содержимого): stackblitz.com/edit/so-one-active-at-time?file=src/App.js - person buzatto; 27.01.2021
comment
Работает очень хорошо, но есть ли способ экспортировать этот контент? Мне нужно разобраться с ЕСЛИ - person luck; 27.01.2021
comment
Необходимо сделать if (options === latest) {return ...} на другой странице. - person luck; 27.01.2021
comment
На самом деле это другой вопрос, который зависит от того, как вы создали приложение, чтобы найти то, что лучше всего для вас. вы можете перейти в состояние общего родительского const [selected, setSelected] = useState(null); и передать его дочерним компонентам в качестве свойств (это называется состоянием подъема). Вы также можете использовать Context API, чтобы избежать сверления опор, таким образом вы можете получить доступ непосредственно к своим компонентам. - person buzatto; 27.01.2021
comment
состояние подъема: reactjs.org/docs/lifting-state-up.html | контекстный API: reactjs.org/docs/context.html - person buzatto; 27.01.2021
comment
В приложении есть redux, но я избегал его использовать, у меня нет большого опыта работы с Redux, а с redux имеет дело другой парень. - person luck; 27.01.2021
comment
redux предназначен для более сложного управления состоянием, что, похоже, не так. сначала вы можете сделать, как я говорю, больше состояния вверх. если есть много уровней компонентов, которые вы должны передать свои реквизиты selected и setSelected, то подумайте об использовании Context API - person buzatto; 27.01.2021
comment
я редактировал stackblitz.com/edit/so- one-active-at-time? file = src / App.js, где я поднимаю состояние в приложение, чтобы дать вам пример - person buzatto; 27.01.2021
comment
stackblitz.com/edit/react-2xyswf - person luck; 27.01.2021
comment
Это приложение не будет работать, но оно просто объясняет, что я делаю. Я использую константу orderContacts, которая имеет дело с фильтрами, типами контактов и порядком. Мне нужно, чтобы каждый раз, когда нажимается кнопка сортировки, она меняет сортировку. - person luck; 27.01.2021
comment
Простите, не знаю, смогу ли я объяснить, дело в том, что проект довольно большой. Этот orderContact находится в другом файле, я вызываю компонент ModalOrder следующим образом: ‹ModalOrder orderContacts = {orderContacts} modalIsOpen = {modalIsOpenOrder} setModalIsOpen = {setModalIsOpenOrder} /› - person luck; 27.01.2021
comment
Я бы посоветовал вам рассмотреть для этого Context API. у вас есть хорошие статьи в Интернете о том, как подать заявку, и несколько ТАК вопросов по этому поводу. если даже после этого кажется, что это не решает, вы можете создать другой вопрос с этой конкретной проблемой. Я не хочу показаться недобрым, но ваш вопрос выглядит ответом, и вам следует подумать о том, какой ответ, по вашему мнению, соответствует вашим потребностям, и отметить его как правильный. Если возникают другие вопросы, лучше создать другой вопрос с определенным объемом, чем связывать множество последующих вопросов, в которых вы теряете свою первоначальную цель. - person buzatto; 27.01.2021
comment
да, я уже сделал это stackoverflow.com/questions/65926633/ спасибо братан, мне просто нужно посмотреть, что я буду делать сейчас - person luck; 27.01.2021

Вместо этого используйте только одну переменную состояния.

const [indexSelected, setIndexSelected] = useState(-1);
<Button onClick={() => setIndexSelected(indexSelected === 1 ? -1 : 1)} borderT="1px solid rgb(196, 196, 196, 0.8)" borderB="1px solid rgb(196, 196, 196, 0.8)" height="32px" width="100%" align="center" justify="start"> 
  <Row display={indexSelected === 1 ? "flex" : "none"} shadow="2px 0px 4px rgba(0, 0, 0, 0.1);"  custom="border-bottom-right-radius: 5px; border-top-right-radius: 5px; position: absolute; left: 0;" width="10px" height="32px" background="#448757" /> 

Следуйте тому же шаблону и для остальных кнопок.

person Snow    schedule 26.01.2021
comment
Могу ли я сделать это, если useState ({type: false, order: false}) ?? - person luck; 26.01.2021
comment
Цель - указать, что используется, с именем свойства вместо индекса? В этом случае вы можете вместо этого использовать строку в состоянии, например setNameSelected(nameSelected === 'type' ? '' : 'type') - person Snow; 26.01.2021
comment
Да, потому что мне нужно идентифицировать каждую кнопку, чтобы выполнить IF позже. Потому что он установит эту опцию для onClick, заставив useState изменить тип / порядок и каким-то образом изменить отображение гибкости / отсутствия зеленой визуальной панели. - person luck; 26.01.2021
comment
зеленый var появляется слева от опции - person luck; 26.01.2021
comment
Если вы используете в ответе метод индексации, вы можете идентифицировать кнопку, проверив число indexSelected. Для первой кнопки у вас есть indexSelected === 1 ? "flex" : "none". Для второй кнопки у вас есть indexSelected === 2 ? "flex" : "none" и так далее. - person Snow; 26.01.2021
comment
Или вы можете использовать строковый метод, используя такие же сравнения. - person Snow; 26.01.2021

На мой взгляд, вам стоит подумать об использовании useReducer Hook

реагировать на документы: https://reactjs.org/docs/hooks-reference.html#usereducer

person Ahmed Elabbasy    schedule 26.01.2021