Ответы пользователя по тегу React
  • Как избежать замыкания в колбеке в EventListener?

    0xD34F
    @0xD34F Куратор тега React
    Комментировать
  • Как по кнопке вызвать скролл в таблице Ant Design до первой строки?

    0xD34F
    @0xD34F Куратор тега React
    tableRef.current.querySelector('.ant-table-body').scrollTop = 0;
    Ответ написан
  • Как реализовать отображение компонентов из массива?

    0xD34F
    @0xD34F Куратор тега React
    сделал две функции, которые поочерёдно возвращают компонент согласно индексу

    Возвращают куда? Что с возвращённым происходит? Да ничего.

    желаемого эффекта не получил

    А как родительский компонент узнает, что при изменении индекса надо выполнить рендеринг? Да никак.

    в консоль выводиться...

    Не понимаю, зачем вы это сказали. Никакого кода с выводом в консоль показано не было.

    Исправляем: в массив складываем сами компоненты вместо их экземпляров; индекс делаем состоянием; по клику на кнопки обновляем индекс; используя индекс извлекаем из массива компонент. Всё:

    const Component1 = () => <h1>hello, world!!</h1>;
    const Component2 = () => <h1>fuck the world</h1>;
    const Component3 = () => <h1>fuck everything</h1>;
    
    const components = [ Component1, Component2, Component3 ];
    
    function App() {
      const [ index, setIndex ] = useState(0);
      const min = 0;
      const max = components.length - 1;
    
      const Component = components[index];
    
      const onClick = ({ target: { dataset: { step } } }) =>
        setIndex(Math.max(min, Math.min(max, index + +step)));
    
      return (
        <div>
          <button onClick={onClick} data-step="-1" disabled={index <= min}>prev</button>
          <button onClick={onClick} data-step="+1" disabled={index >= max}>next</button>
          {Component && <Component />}
        </div>
      );
    }
    Ответ написан
    Комментировать
  • Как поменять надпись на кнопке через 4 секунды?

    0xD34F
    @0xD34F Куратор тега React
    const [ buttonText, setButtonText ] = useState('hello, world!!');
    const [ clicked, setClicked ] = useState(false);
    
    function onClick() {
      setClicked(true);
      setTimeout(() => {
        setButtonText('fuck the world');
        setClicked(false);
      }, 1000);
    }

    <button onClick={onClick} disabled={clicked}>{buttonText}</button>
    {clicked ? <img src="..." /> : null}
    Ответ написан
    2 комментария
  • Как изменить порядок вызова обработчиков событий?

    0xD34F
    @0xD34F Куратор тега React
    Смотрим, что умеет addEventListener:

    useCapture

    A boolean value indicating whether events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree.

    Соответственно, element.addEventListener(event, handler, true).
    Ответ написан
    Комментировать
  • Почему state не обновляется корректно с использованием React.memo + useCallback?

    0xD34F
    @0xD34F Куратор тега React
    Всё обновляется корректно. Что написали, то и получили.

    Почему всегда рендерятся все экземпляры Child2 - потому что вместо обновления одного элемента data.list вы всегда обновляете все.

    Почему значения сбрасываются на дефолтные - потому что из-за useCallback data у вас всегда имеет начальное значение (ну, то, что было при первом рендеринге, то, которое initialData).

    Переделываем:

    Компонент Parent, обработчик onChange передаваемый в Child1, пусть вместо значения обновляемого свойства получает функцию, которая будет принимать предыдущее значение и возвращать новое:

    React.useCallback((key, val) => {
      setData(data => ({
        ...data,
        [key]: val(data[key]),
      }));
    }, [])

    Ну и переписываем его вызовы внутри Child1:

    onChange('list', list => list.map((n, i) => i === index
      ? { ...n, [prop]: value }
      : n
    ))

    onChange('description', () => e.target.value)
    Ответ написан
  • Как правильно работать с контекстом в react?

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

    Не "удаляем" - создаётся новый массив, в котором может оказаться (а может и нет) на один элемент меньше. Не "иногда" - всегда выбор нового случайного элемента осуществляется из старого массива.

    Как правильно реализовать такую логику?

    Надо выбирать новый случайный элемент из нового массива, а не из старого. Переносим соответствующий вызов dispatch из обработчика клика в эффект:

    useEffect(() => {
      dispatch({
        type: SET_RND_NUM,
        payload: state.arr[Math.random() * state.arr.length | 0],
      });
    }, [ state.arr ]);
    Ответ написан
  • Как сделать активацию кнопки после нажатия на checkbox?

    0xD34F
    @0xD34F Куратор тега React
    const [ checked, setChecked ] = React.useState(false);

    <input
      type="checkbox"
      checked={checked}
      onChange={e => setChecked(e.target.checked)}
      ...

    <button
      disabled={!checked}
      ...
    Ответ написан
    3 комментария
  • Почему выдает "TypeError: products.filter is not a function"?

    0xD34F
    @0xD34F Куратор тега React
    function Home(products, 
        searchValue,
        ...

    Круто.

    Откройте документацию и разберитесь, какой формат имеют входные данные компонента. Ну или хотя бы погуглите примеры использования props'ов.
    Ответ написан
    Комментировать
  • Как использовать один компонент но с частью данных?

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

    const Todos = ({ todos, TodoItem }) => (
      <div className="todos">
        {todos.map(n => (
          <div className="todo-item" key={n.id}>
            <TodoItem todo={n} />
          </div>
        ))}
      </div>
    );

    const HeaderTodoItem = ({ todo }) => (
      <h3>{todo.title}</h3>
    );

    const AppTodoItem = ({ todo }) => (
      <>
        <img src={todo.img} alt={todo.title} />
        <div>
          <h3>{todo.title}</h3>
          <p>{todo.text}</p>
        </div>
      </>
    );

    <Todos todos={todos} TodoItem={HeaderTodoItem} />

    <Todos todos={todos} TodoItem={AppTodoItem} />
    Ответ написан
    Комментировать
  • Как можно сократить этот код?

    0xD34F
    @0xD34F Куратор тега React
    const SORT_FUNCS = {
      priceAsc: (a, b) => a.price - b.price,
      priceDesc: (a, b) => b.price - a.price,
      formedAsc: (a, b) => a.formed_in - b.formed_in,
      formedDesc: (a, b) => b.formed_in - a.formed_in,
    };

    const data = useMemo(() => {
      const sortFunc = SORT_FUNCS[sortType];
      return sortFunc ? [...bands].sort(sortFunc) : bands;
    }, [ sortType ]);
    Ответ написан
    Комментировать
  • React как сделать фильтрацию по дате и автору?

    0xD34F
    @0xD34F Куратор тега React
    const [ author, setAuthor ] = useState(null);
    const [ dateMin, setDateMin ] = useState(null);
    const [ dateMax, setDateMax ] = useState(null);
    
    const authors = useMemo(
      () => [...new Set(articles.map(n => n.author))],
      [ articles ]
    );
    const filteredArticles = useMemo(
      () => [
        [  author, n => n.author === author      ],
        [ dateMin, n => n.publishedAt >= dateMin ],
        [ dateMax, n => n.publishedAt <= dateMax ],
      ].reduce((acc, n) => n[0] ? acc.filter(n[1]) : acc, articles),
      [ articles, author, dateMin, dateMax ]
    );

    <select value={author} onChange={e => setAuthor(e.target.value)}>
      <option></option>
      {authors.map(n => <option>{n}</option>)}
    </select>
    
    от <input type="date" value={dateMin} onChange={e => setDateMin(e.target.value)} />
    до <input type="date" value={dateMax} onChange={e => setDateMax(e.target.value)} />

    {filteredArticles.map(n => <Card {...n} />)}
    Ответ написан
    3 комментария
  • Как динамически переключать Autoplay?

    0xD34F
    @0xD34F Куратор тега React
    const [ autoplay, setAutoplay ] = useState(false);
    const swiper = useRef();
    
    useEffect(() => {
      swiper.current.autoplay[autoplay ? 'start' : 'stop']();
    }, [ autoplay ]);

    <input
      type="checkbox"
      checked={autoplay}
      onChange={e => setAutoplay(e.target.checked)}
    />

    <Swiper
      onSwiper={instance => swiper.current = instance}
      ...
    >
    Ответ написан
  • Как вставить данные из массива в таблицу?

    0xD34F
    @0xD34F Куратор тега React
    const data = Object.entries(сюда кидаете свои данные);
    
    const years = Array
      .from(new Set(data.flatMap(n => Object.keys(n[1].G))))
      .sort((a, b) => a - b);
    
    const columns = Array
      .from(new Set(data.flatMap(n => Object.values(n[1].G).flatMap(Object.keys))))
      .sort();

    <TableHead>
      <TableRow>
        <TableCell rowSpan={2}>regions</TableCell>
        {years.map(n => <TableCell colSpan={columns.length}>{n}</TableCell>)}
      </TableRow>
      <TableRow>
        {years.flatMap(n => columns.map(m => <TableCell>{m}</TableCell>))}
      </TableRow>
    </TableHead>
    <TableBody>
      {data.map(([ region, { G } ]) => (
        <TableRow>
          <TableCell>{region}</TableCell>
          {years.flatMap(n => columns.map(m => <TableCell>{G[n]?.[m]?.value ?? 0}</TableCell>))}
        </TableRow>
      ))}
    </TableBody>
    Ответ написан
    1 комментарий
  • Какую логику сделать для проверки верности вопроса в приложении тест?

    0xD34F
    @0xD34F Куратор тега React
    Начать следует не с какой-то там логики, а со структуры данных, что содержит вопросы. То, что есть сейчас, никуда не годится. Что, если завтра вместо трёх вариантов ответа надо будет сделать четыре? Будете добавлять ещё по два свойства в каждый объект в массиве вопросов, а в компоненте закопипастите ещё один input? А если в разных вопросах должно будет быть разное количество вариантов ответа? Что тогда?

    Варианты ответа складываете в массив, а корректный вариант обозначаете через его индекс:

    const questions = [
      {
        text: 'Выберите верное утверждение',
        answers: [
          'СССР распался в 1997 году',
          'Солнце вращается вокруг Земли',
          'шестью восемь - двадцать три',
        ],
        correctAnswer: 1,
      },
      {
        text: '...',
        answers: [ '...', '...', ... ],
        correctAnswer: ...,
      },
      ...
    ];

    В компоненте вопроса вместо того, чтобы копипастить input'ы, делаете цикл по вариантам ответа:

    function Question(props) {
      const onChange = e => props.onAnswerChange(+e.target.value);
    
      return (
        <div>
          <h3>{props.question.text}</h3>
          <ol>
            {props.question.answers.map((n, i) => (
              <li>
                <label>
                  <input
                    type="radio"
                    value={i}
                    checked={props.answer === i}
                    onChange={onChange}
                  />
                  {n}
                </label>
              </li>
            ))}
          </ol>
        </div>
      );
    }

    В родительском компоненте храните массив ответов:

    function App(props) {
      const [ answers, setAnswers ] = useState(Array(props.questions.length).fill(null));
    
      const updateAnswer = (questionIndex, answer) =>
        setAnswers(answers.map((n, i) => i === questionIndex ? answer : n));
    
      return (
        <div>
          {props.questions.map((n, i) => (
            <Question
              question={n}
              answer={answers[i]}
              onAnswerChange={answer => updateAnswer(i, answer)}
            />
          ))}
        </div>
      );
    }

    Ну и возвращаясь к вашему вопросу, чего там надо было?

    не получается сделать грамотную проверку правильного ответа...

    Чтобы проверить правильность ответа, надо сравнить его со значением свойства correctAnswer у соответствующего (с тем же индексом) вопроса. Например, считаем количество правильных ответов:

    const correctAnswersCount = answers.reduce((acc, n, i) => {
      return acc + (n === questions[i].correctAnswer);
    }, 0);

    ...и одновременно вывести верные ответы

    Поскольку свойство, обозначающее правильный ответ, является его индексом, просто достаём соответствующие элементы из массивов с вариантами ответа:

    const correctAnswers = questions.map(n => n.answers[n.correctAnswer]);

    Ну а массив строк вывести - надеюсь, справитесь сами.

    UPD. Посмотреть живьём можно здесь (есть отличия от того, что есть или предполагается у вас - вопросы показываются по очереди, а не все сразу; в результатах отображаются только верные ответы).
    Ответ написан
    1 комментарий
  • Как указать активность элемента?

    0xD34F
    @0xD34F Куратор тега React
    В качестве дефолтного значения для activeType указывайте не 0, а нулевой элемент из types:

    React.useState(0); ---> React.useState(types[0]);
    Ответ написан
    1 комментарий
  • Как объединить данные из двух массивов по id?

    0xD34F
    @0xD34F Куратор тега React
    const getData = type => fetch(`https://jsonplaceholder.typicode.com/${type}`).then(r => r.json());

    const [ data, setData ] = useState([]);
    
    useEffect(() => {
      Promise
        .all([ 'posts', 'users' ].map(getData))
        .then(([ posts, users ]) => {
          const usersObj = Object.fromEntries(users.map(n => [ n.id, n ]));
          setData(posts.map(n => ({
            post: n,
            user: usersObj[n.userId],
          })));
        });
    }, []);
    
    return (
      <div>
        {data.map(({ post, user }) => (
          <div>
            <h2>{post.title}</h2>
            <h3>{user.name}</h3>
            <p>{post.body}</p>
          </div>
        ))}
      </div>
    );
    Ответ написан
    1 комментарий
  • Как модифицировать мой API запрос?

    0xD34F
    @0xD34F Куратор тега React
    function Pokemon({ name, url }) {
      const [ data, setData ] = useState(null);
    
      useEffect(() => {
        fetch(url).then(r => r.json()).then(setData);
      }, [ url ]);
    
      return (
        <div>
          <h4>{name}</h4>
          {data
            ? <div>
                <ul>{data.abilities.map(n => <li>{n.ability.name}</li>)}</ul>
                <img src={data.sprites.front_default} />
              </div>
            : <div>loading...</div>
          }
        </div>
      );
    }
    Ответ написан
    Комментировать
  • Как создать фильтр для продуктов?

    0xD34F
    @0xD34F Куратор тега React
    Кроме полных данных храните также и отфильтрованные. Их и отображайте. Значение фильтра пусть хранится в родителе - передавайте его в экземпляр компонента фильтра вместе с функцией, которая его (значение) будет обновлять. При обновлении значения фильтра обновляете массив отфильтрованных данных.

    const Filter = ({ title, values, value, onChange }) => (
      <div>
        <h3>{title}</h3>
        {values.map(n => (
          <label>
            <input
              type="radio"
              onChange={() => onChange(n)}
              checked={value === n}
            />
            {n}
          </label>
        ))}
      </div>
    );

    state = {
      ...
      filteredProducts: [],
      status: 'all',
    }
    
    onFilterStatusChange = status => {
      this.setState({ status });
    }
    
    filterProducts() {
      this.setState(({ status }) => ({
        filteredProducts: status === 'all'
          ? this.state.products
          : this.state.products.filter(n => n.prod_status.includes(status)),
      }));
    }
    
    componentDidUpdate(prevProps, prevState) {
      if (this.state.status !== prevState.status) {
        this.filterProducts();
      }
    }
    
    render() {
      ...
        <Filter
          title="Status:"
          values={[ 'all', 'recommended', 'saleout', 'bestseller', 'new' ]}
          value={this.state.status}
          onChange={this.onFilterStatusChange}
        />
        <Products products={this.state.filteredProducts} />
      ...
    }
    Ответ написан
  • Как разделить строку на две и вывести их как два разных блока?

    0xD34F
    @0xD34F Куратор тега React
    Вместо <div className="status">{status}</div> пусть будет

    {status.split(',').map(n => <div className="status">{n}</div>)}
    Ответ написан