Задать вопрос
Ответы пользователя по тегу React
  • Как присвоить класс к выбранному input-radio в react-js?

    0xD34F
    @0xD34F Куратор тега React
    Если выбор сводится к да/нет, то почему должны быть две радиокнопки, а не один чекбокс? Но окей, вам виднее.

    Складываем в массивы описания радиокнопок:

    const presence = [
      { value:    'coming', text:    'Приду' },
      { value: 'notComing', text: 'Не приду' },
    ];
    
    const transport = [
      { value: 'yes', text:  'Да' },
      { value:  'no', text: 'Нет' },
    ];

    Создаём стейт с данными формы и метод его обновления:

    const [ formData, setFormData ] = useState({});
    const onChange = ({ target: t }) =>
      setFormData(fd => ({
        ...fd,
        [t.name]: t.value,
      }));

    Создаём компоненты радиокнопки и радиогруппы:

    const Radio = ({ children, className, ...props }) =>
      <label className={className ?? ''}>
        <input type="radio" {...props} />
        {children}
      </label>;
    
    const RadioGroup = ({ title, name, items, value, onChange }) =>
      <div className="radiogroup">
        <div className="radiogroup-title">{title}</div>
        {items.map(n => (
          <Radio
            key={n.value}
            className={`radiogroup-item ${value === n.value ? 'active' : ''}`}
            name={name}
            value={n.value}
            checked={value === n.value}
            onChange={onChange}
          >
            {n.text}
          </Radio>
        ))}
      </div>;

    Создаём радиогруппы, используя массивы с описаниями радиокнопок:

    <RadioGroup
      title="Подтвердите присутствие"
      name="presence"
      items={presence}
      value={formData.presence}
      onChange={onChange}
    />
    <RadioGroup
      title="Нужен ли трансфер до места проведения?"
      name="transport"
      items={transport}
      value={formData.transport}
      onChange={onChange}
    />

    Всё.
    Ответ написан
    1 комментарий
  • Как обновлять дочерние элементы которые принимают значение функции?

    0xD34F
    @0xD34F Куратор тега React
    при изменении language у меня не меняются переводы

    Меняются. С запаздыванием на один шаг. Потому что texts обновляется в эффекте, т.е., после того, как новая версия t будет создана. Так что t получает ссылку на старую версию texts.

    Никакого texts не надо, доставайте в t нужный объект из translations напрямую:

    const t = useCallback(
      key => key
        .split('.')
        .reduce((p, c) => p?.[c], translations[language]),
      [ language ]
    );
    Ответ написан
    Комментировать
  • Как сделать динамический текст над слайдером?

    0xD34F
    @0xD34F Куратор тега React
    использую библиотеку rc-slider, к сожалению она не предлагает возможности...

    А если изучить документацию повнимательнее? Есть пример с выводом значения в тултипе при перетаскивании, сделать тултип постоянно видимым не проблема.
    Ответ написан
    1 комментарий
  • Как в react выделить все чекбоксы?

    0xD34F
    @0xD34F Куратор тега React
    const Checkbox = forwardRef(({ label, ...props }, ref) =>
      <label>
        <input type="checkbox" ref={ref} {...props} />
        {label}
      </label>
    );
    
    function CheckboxGroup({
      items,
      label = item => item,
      selected,
      setSelected,
    }) {
      const onChange = ({ target: { checked, dataset: { index } } }) =>
        setSelected(selected => checked
          ? [ ...selected, items[index] ]
          : selected.filter(n => n !== items[index])
        );
    
      const allSelectedRef = useRef();
      const onAllSelectedChange = ({ target: { checked } }) =>
        setSelected(checked ? [...items] : []);
    
      useEffect(() => {
        const isAllSelected = items.length === selected.length;
        allSelectedRef.current.checked = isAllSelected;
        allSelectedRef.current.indeterminate = !isAllSelected && !!selected.length;
      }, [ selected ]);
    
      return (
        <div className="checkbox-group">
          <Checkbox
            label="SELECT ALL"
            defaultChecked={false}
            onChange={onAllSelectedChange}
            ref={allSelectedRef}
          />
          {items.map((n, i) => (
            <Checkbox
              label={label(n)}
              data-index={i}
              checked={selected.includes(n)}
              onChange={onChange}
            />
          ))}
        </div>
      );
    }

    https://jsfiddle.net/kj9wet6L/
    Ответ написан
    Комментировать
  • Как мне сделать так, чтобы slice был для каждого компонента свой?

    0xD34F
    @0xD34F Куратор тега React
    кликая на один список открывается и второй

    Ну ещё бы не открывался. А как конкретно накосячили - забыли передать dropdownId или тупо скопипастили уже существующий?

    Если каждому экземпляру Dropdown передавать уникальный dropdownId, то открываться будет один. Если открываться должны независимо, то вместо хранения одного id можно сделать объект, где id будут ключами.
    Ответ написан
  • Как осуществлять фильтрацию по нескольким свойствам?

    0xD34F
    @0xD34F Куратор тега React
    Вместо общего массива выбранных значений сделать отдельные для каждого из свойств.

    Чтобы не дублировались чекбоксы, создавать их на основе уникализированных массивов значений свойства вместо массива данных.

    Чтобы поменьше копипастить, оформить чекбоксы в отдельный компонент - принимает массив доступных значений, массив выбранных значений, функцию установки выбранных значений.

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

    https://jsfiddle.net/bqyxa0kL/
    Ответ написан
    Комментировать
  • Как реализовать эффект печатающегося текста?

    0xD34F
    @0xD34F Куратор тега React
    Состояние компонента должно содержать три значения - индекс печатаемой строки, количество отображаемых символов, направление печати (печатаем или стираем, т.е., в какую сторону надо изменять количество отображаемых символов, увеличивать или уменьшать). Дальше понятно что - setInterval или рекурсивный setTimeout, изменяем количество отображаемых символов, если дошли до конца строки - меняем направление, дошли до начала - меняем направление и индекс строки. Вот как-то так:

    const defaults = {
      index: 0,
      length: 0,
      step: 1,
    };
    
    function Typewriter({ strings, delay }) {
      const [ state, setState ] = useState(null);
    
      useEffect(() => {
        setState(() => ({...defaults}));
      }, [ strings ]);
    
      useEffect(() => {
        const timeoutId = setTimeout(setState, delay, ({...state}) => {
          state.length += state.step;
          if (state.length === strings[state.index].length) {
            state.step = -1;
          } else if (state.length === 0) {
            state.step = 1;
            state.index = (state.index + 1) % strings.length;
          }
          return state;
        });
    
        return () => clearTimeout(timeoutId);
      });
    
      return <div>{strings?.[state?.index]?.slice(0, state?.length)}</div>;
    }
    Ответ написан
    8 комментариев
  • Как сделать так, чтобы по потери фокуса input'ом измененный текст попадал в соответствующий li?

    0xD34F
    @0xD34F Куратор тега React
    Надо запоминать индекс кликнутого li.

    const defaultEdit = {
      index: -1,
      value: '',
    };
    
    function App() {
      const [ notes, setNotes ] = useState([...'12345']);
      const [ edit, setEdit ] = useState(defaultEdit);
    
      const onClick = ({ currentTarget: { dataset: { index } } }) =>
        setEdit({ index: +index, value: notes[index] });
    
      const onChange = ({ target: { value } }) =>
        setEdit(edit => ({ ...edit, value }));
    
      const onBlur = () => {
        setNotes(notes => notes.map((n, i) => i === edit.index ? edit.value : n));
        setEdit(defaultEdit);
      };
    
      return (
        <div>
          <ul>{notes.map((n, i) => (
            <li data-index={i} onClick={onClick}>
              {n}
            </li>))}
          </ul>
          <input value={edit.value} onChange={onChange} onBlur={onBlur} />
        </div>
      );
    }
    Ответ написан
  • Как подгрузить данные из объекта?

    0xD34F
    @0xD34F Куратор тега React
    Объект менять не нужно.

    Нужно.

    Так будет лучше, просто поверьте:

    const SERIES = [
      {
        comment: '1 сезон',
        folder: [
          { comment: '1 серия' },
          { comment: '2 серия' },
        ],
      },
      {
        comment: '2 сезон',
        folder: [
          { comment: '1 серия' },
          { comment: '2 серия' },
          { comment: '3 серия' },
        ],
      },
    ];
    
    const ItemsList = ({ header, items, active, onChange }) => (
      <div>
        <h3>{header}</h3>
        <ul className="items">
          {items.map(({ comment }, i) => (
            <li
              className={`item ${active === i ? 'active' : ''}`}
              onClick={() => onChange?.(i !== active ? i : -1)}
            >
              {comment}
            </li>
          ))}
        </ul>
      </div>
    );
    
    function App() {
      const [ iSeason, setActiveSeason ] = useState(-1);
      const [ iEpisode, setActiveEpisode ] = useState(-1);
      const season = SERIES[iSeason];
      const episode = season?.folder[iEpisode];
    
      useEffect(() => {
        setActiveEpisode(-1);
      }, [ iSeason ]);
    
      return (
        <div>
          <ItemsList
            header="сезоны"
            items={SERIES}
            active={iSeason}
            onChange={setActiveSeason}
          />
          {season && <ItemsList
            header="серии"
            active={iEpisode}
            onChange={setActiveEpisode}
            items={season.folder}
          />}
          {episode && <div>Выбрали: {season.comment}, {episode.comment}</div>}
        </div>
      );
    }
    Ответ написан
  • Почему при обновлении состояния не рендерится компонент?

    0xD34F
    @0xD34F Куратор тега React
    Не выдумывайте, всё рендерится.

    Почему видимых изменений нет? Потому что обновляете одно состояние, а рендер выполняете на основе другого, items в App и items в ElementsList - это разные массивы. Не надо никакого useState в ElementList, выполняйте рендер на основе prop'а; метод onDelete перенесите в App и тоже передавайте в ElementList через props.
    Ответ написан
    1 комментарий
  • Как изменить состояние дочернего компонента из родителя?

    0xD34F
    @0xD34F Куратор тега React
    Добавьте в дочерний компонент эффект, который будет зависеть от props'ов и обновлять состояние:

    useEffect(() => setValue(props.value), [ props.value ]);

    Это если прямо отвечать на спрошенное. Но действительно ли дочерний компонент должен хранить значение input'а у себя? Возможно, вы не конца понимаете, чего хотите, и вам надо узнать про подъём состояния.
    Ответ написан
    3 комментария
  • Как вывести вложенные данные в виде таблицы с объединёнными ячейками?

    0xD34F
    @0xD34F Куратор тега React
    const rowspan = shards => shards.reduce((acc, n) => acc + n.jobs.length, 0);

    <table>
      <thead>
        <tr>{HEADERS.map(n => <th>{n}</th>)}</tr>
      </thead>
      <tbody>{
        DATA.flatMap(({ cloud, shards }) =>
        shards.flatMap((shard, iShard) =>
        shard.jobs.map((job, iJob) =>
          <tr>
            {!iShard && !iJob && <td rowSpan={rowspan(shards)}>{cloud}</td>}
            {!iJob && <td rowSpan={shard.jobs.length}>{shard.repository}</td>}
            {!iJob && <td rowSpan={shard.jobs.length}>{shard.repository_size_gb}</td>}
            <td>{job.job}</td>
            <td>{job.job_description}</td>
            <td>{job.backup_name}</td>
            <td>{job.backup_size_gb}</td>
          </tr>
        )))}
      </tbody>
    </table>

    https://jsfiddle.net/0ycjmnf3/
    Ответ написан
    Комментировать
  • Как в ивентах React отслеживать погружение - capture?

    0xD34F
    @0xD34F Куратор тега React
    Документация подсказывает, что есть такая штука, как

    onClickCapture: A version of onClick that fires in the capture phase.
    Ответ написан
    Комментировать
  • Как установить дефолтные точки на гугл карте api?

    0xD34F
    @0xD34F Куратор тега React
    fitBounds:

    // это массив ваших "точек"
    const locations = [
      { lat: ..., lng: ... },
      { lat: ..., lng: ... },
      ...
    ];

    const onLoad = map => {
      const bounds = new window.google.maps.LatLngBounds();
      locations.forEach(n => bounds.extend(n));
      map.fitBounds(bounds);
    };

    <GoogleMap
      onLoad={onLoad}
      ...
    >
      {locations.map(n => <Marker position={n} />)}
    </GoogleMap>
    Ответ написан
    Комментировать
  • Как организовать вывод элементов компонента react по 4 штуки?

    0xD34F
    @0xD34F Куратор тега React
    const useChunked = (data, chunkSize) =>
      useMemo(
        () => Array.prototype.reduce.call(
          data,
          (acc, n, i) => ((acc[i / chunkSize | 0] ??= []).push(n), acc),
          []
        ),
        [ data, chunkSize ]
      );

    const ChunkedListItem = ({ item }) =>
      <pre>{JSON.stringify(item, null, 2)}</pre>;
    
    const ChunkedList = ({ items, inRow, Item = ChunkedListItem }) => {
      const rows = useChunked(items, inRow);
    
      return (
        <div className="chunked-list">
          {rows.map(row => (
            <ul>
              {row.map(n => <li><Item item={n} /></li>)}
            </ul>
          ))}
        </div>
      );
    }

    https://jsfiddle.net/7pq92ud8/1/
    Ответ написан
    Комментировать
  • Как реализовать react-easy-sort?

    0xD34F
    @0xD34F Куратор тега React
    const [ items, setItems ] = useState([]);
    const [ src, setSrc ] = useState('');
    
    const onChange = e => {
      setSrc(e.target.value);
    };
    
    const onClick = () => {
      setSrc('');
      setItems([
        ...items,
        {
          id: 1 + Math.max(0, ...items.map(n => n.id)),
          src,
        },
      ]);
    };
    
    const onSortEnd = (iOld, iNew) => {
      setItems(([...items]) => (
        items.splice(iNew, 0, items.splice(iOld, 1)[0]),
        items
      ));
    };

    <div>
      <input value={src} onChange={onChange} />
      <button onClick={onClick}>add</button>
    </div>
    <SortableList onSortEnd={onSortEnd}>
      {items.map(n => (
        <SortableItem key={n.id}>
          <img src={n.src} />
        </SortableItem>
      ))}
    </SortableList>
    Ответ написан
  • Как переключаться между компонентами?

    0xD34F
    @0xD34F Куратор тега React
    const languages = [
      { name:  'Ru', Component: AppRu  },
      { name: 'Eng', Component: AppEng },
      ...
    ];

    const [ language, setLanguage ] = useState(0);
    const { name, Component } = languages[language];
    const onClick = () => setLanguage((language + 1) % languages.length);

    <button onClick={onClick}>{name}</button>
    <Component />
    Ответ написан
    Комментировать
  • Почему не работают JSS стили Loader?

    0xD34F
    @0xD34F Куратор тега React
    Как может работать то, что вы не используете?

    <div className={classes.bar1}></div>

    Класс bar1 есть, это хорошо. А почему нет bar? Плохо.

    UPD. Ну и в целом многословно как-то. Зачем копипастить по двенадцать раз одно и то же? В createUseStyles заменяем все barN на

    ...Object.fromEntries(Array.from({ length: 12 }, (_, i) => [
      `bar${i + 1}`,
      {
        transform: `rotate(${i * 30}deg) translate(0, -130%)`,
        animationDelay: `${i * 0.1 - 1.2}s`,
      },
    ])),

    А в компоненте Loader пусть будет так:

    <div className={`${classes.loader} js--loader`}>
      {Array.from(
        { length: 12 },
        (_, i) => <div className={`${classes.bar} ${classes[`bar${i + 1}`]}`}></div>
      )}
    </div>
    Ответ написан
    Комментировать
  • Как избавиться от бага - всплывающая подсказка не убирается по первому клику?

    0xD34F
    @0xD34F Куратор тега React
    Возникает проблема, что нужно нажать 2 раза на подсказку, чтобы она исчезла.

    Не она, а они. Выбираете подсказку - значение меняется. Поменялось значение - запускается эффект, в котором заново вычисляется массив подсказок, и они отображаются.

    Может, вместо эффекта вам стоит вычислять массив подсказок в обработчике onChange:

    const onChange = ({ target: { value } }) => {
      setVal(value);
      setPrintVariables(value.length > 1
        ? arrayVariables.filter(n => n.includes(value))
        : []
      );
    };

    <input value={val} onChange={onChange} />

    ??
    Ответ написан
    1 комментарий