Ответы пользователя по тегу React
  • Как в 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/
    Ответ написан
    Комментировать
  • Как осуществлять фильтрацию по нескольким свойствам?

    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-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 комментарий
  • Почему не применяются JSS стили к компонентам и элементам?

    0xD34F
    @0xD34F Куратор тега React
    Что, в одно ухо влетело, из другого вылетело? - один из косяков переехал сюда из предыдущего вопроса, вместо . здесь должны быть $:

    '&:checked + .todo-item__span::before'

    '&:checked + .todo-item__span::after'

    Одной пустой строки недостаточно:

    'content': '',

    'content': '""',
    Ответ написан
  • Как передать условие классов в компонент с помощью JSS?

    0xD34F
    @0xD34F Куратор тега React
    'todo-item--checked': {
        'border': '2px solid #2196f3',
        ' box-shadow': '0 0 10px 3px #2196f3',
    },

    'todo-item--checked .todo-item__description': {
        'color': '#6495EDFF',
    },

    'todo-item--checked': {
      'border': '2px solid #2196f3',
      'box-shadow': '0 0 10px 3px #2196f3',
      '& $todo-item__description': {
        'color': '#6495EDFF',
      },
    },


    <div className={`
        ${classes['todo-item__description']}
        ${classes['todo-item--checked .todo-item__description']}
    `}>

    <div className={classes['todo-item__description']}>
    Ответ написан
    Комментировать
  • Почему обработчик реагирует на событие, случившееся до его установки?

    0xD34F
    @0xD34F Куратор тега React
    Я так понимаю, вот она, причина, подъехала с версией 18.0.0:

    Consistent useEffect timing: React now always synchronously flushes effect functions if the update was triggered during a discrete user input event such as a click or a keydown event.

    Т.е., жмёте на кнопку, открывается окно, запускается эффект, устанавливается обработчик клика на document, дальше событие клика по кнопке продолжило всплывать и попало в только что установленный обработчик.

    При добавлении задержки, данная проблема уходит

    Можете ещё попробовать ловить событие в фазе погружения:

    document.addEventListener('click', handleOutsideClick, true);
    return () => document.removeEventListener('click', handleOutsideClick, true);
    Ответ написан
    Комментировать
  • Как отображать один из нескольких компонентов, по выбору пользователя?

    0xD34F
    @0xD34F Куратор тега React
    const components = [
      [   'Day', Components1 ],
      [  'Week', Components2 ],
      [ 'Month', Components3 ],
      [  'Year', Components4 ],
    ];

    const [ active, setActive ] = useState(-1);
    const Component = components[active]?.[1];
    const onClick = e => setActive(+e.target.dataset.index);

    {components.map((n, i) => <button data-index={i} onClick={onClick}>{n[0]}</button>)}
    {Component && <Component />}
    Ответ написан
    3 комментария
  • Почему возникает ошибка 'Warning: Each child in a list should have a unique "key" prop'?

    0xD34F
    @0xD34F Куратор тега React
    ключ уже есть

    А потом раз - и ключа нет, undefined - это не ключ. Откуда берётся undefined? Из-за отсутствия свойства id, значение которого должно быть ключом. Как оно пропадает? Да вот так:

    return {
      ...emojis, count: emoji.count + 1
    }

    С какой целью вы делаете свойствами элемента массива все элементы самого массива?

    Да понятно, с какой.

    Ни с какой. Исправляйте опечатку - вместо ...emojis должно быть ...emoji.
    Ответ написан
    Комментировать