Реакция — упражнение на собеседование

Я получил следующее упражнение React с двумя компонентами в интервью, которое мне не удалось скомпилировать... Вопрос был следующий:

Обновите компонент Counter, чтобы он принимал обратные вызовы onIncrement в качестве реквизита и обеспечивал независимое обновление значений счетчика. Каждый обратный вызов должен принимать одно целочисленное значение в качестве параметра, который является суммой, на которую увеличивается существующее значение счетчика.

Комментарии в коде, но моя проблема заключается в том, как реализовать функцию «onIncrement».

const { Component } = React;
const { render } = ReactDOM;

// state data for 3 counters
const data = [
  { id: 1, value: 1 },
  { id: 2, value: 2 },
  { id: 3, value: 3 }
];

// Counter Component
class Counter extends Component {
  render() {
    const { value } = this.props;
    return (
      <div className = "counter">
        <b>{value}</b>
        <div className = "counter-controls">
          <button className = "button is-danger is-small">-</button>
          //I call the function passed     
          <button className = "button is-success is-small" onClick = {()=>{onIncrement(this.props.value)}}>+</button>
        </div>
      </div>
    );
  }
}

class App extends Component {
  constructor(props, context) {
    super(props, context);
  }
  
   onIncrement=(value)=>{
   //I tried several things here but I did not manage to make it work. I guess that I need also the id of the object...
   }

  render() {
    return (
      <div>
       
        {data.map(counter => ( 
          <Counter 
            key = {counter.id} 
            value = {counter.value} 
             //I pass the callback function to the component
            onIncrement = {this.onIncrement} 

            />
        ))}
      </div>
    );
  }
}


render(
  <App/>
, document.querySelector('#root'))

Где вы столкнулись с проблемой или отсутствием знаний о том, как действовать? Вы не указали конкретную проблему

vsync 18.02.2019 16:21

Итак, в чем ваш вопрос/проблема?

Mitya 18.02.2019 16:21

Отредактировано. Как реализовать функцию onIncrement, извините.

Spaniard 18.02.2019 16:23

Вот подсказка: ваш компонент Counter не ожидает/не использует поддержку onIncrement. Вы должны использовать const { value, onIncrement } = this.props; в методе рендеринга.

RobMasters 18.02.2019 16:24

Правда, я пропустил, я просто забыл об этом, спасибо :), но моя проблема связана с реализацией OnIncrement, там я не в курсе, еще одна подсказка? :)

Spaniard 18.02.2019 16:29

Честно говоря, не передав никакой информации о том, какой счетчик будет обновляться, как вы могли его обновить? Я имею в виду, что когда вы вызываете onIncrement() из Counter Компонента, если вы каким-то образом не передаете информацию, для которой Counter вы увеличиваете, вы не можете ожидать, что узнаете «по волшебству» от App Компонента. Кстати, const data на самом деле находится в состоянии App Component? Потому что вы не создаете состояние в компоненте App

Jolly 18.02.2019 16:48
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
6
6
1 220
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

По сути, вы захотите использовать id как способ определить, какие value вам нужно обновить. Как вы это настроили, вы не сможете узнать, какой value нужно обновить (потому что вы не знаете, какой id был нажат), и value не будет сохранен.

ПРИМЕЧАНИЕ: в приведенном ниже примере берется id из event.target.id и value из event.target.value, которые затем деконструируются в обратном вызове handleChangeEvent. Это более распространенное и элегантное решение, чем передача значения в callback, а затем передача его и другого значения другому callback (больше работы, больше кода, но та же функциональность).


Лучшее решение: https://codesandbox.io/s/rjmx8vw99p

компоненты/UpdateQuantity.js

import React, { Component, Fragment } from "react";

export default class App extends Component {
  state = {
    items: [
      { id: "Apples", quantity: 0 },
      { id: "Strawberries", quantity: 0 },
      { id: "Grapes", quantity: 0 },
      { id: "Apricots", quantity: 0 }
    ]
  };

  handleChangeEvent = ({ target: { id, value } }) => {
    this.setState(prevState => ({
      items: prevState.items.map(item => {
        const nextVal = item.quantity + ~~value; // ~~ === parseInt(val, 10) -- required because the "value" is turned into a string when placed on a DOM element

        return id === item.id
          ? { id, quantity: nextVal > 0 ? nextVal : 0 }
          : { ...item };
      })
    }));
  };

  render = () => (
    <div className = "container">
      <h1>Updating Values Inside Array</h1>
      {this.state.items.map(({ id, quantity }) => (
        <div key = {id} className = "container">
          <div>
            {id} ({quantity})
          </div>
          <button
            id = {id}
            value = {1}
            style = {{ marginRight: 10 }}
            className = "uk-button uk-button-primary"
            onClick = {this.handleChangeEvent}
          >
            +
          </button>
          <button
            id = {id}
            value = {-1}
            style = {{ marginRight: 10 }}
            className = "uk-button uk-button-danger"
            onClick = {this.handleChangeEvent}
          >
            -
          </button>
        </div>
      ))}
    </div>
  );
}

Другое решение: https://codesandbox.io/s/yq961275rv (не рекомендуется, так как для этого требуется дополнительный компонент и дополнительный обратный вызов, НО в методе render не требуется ни привязки, ни анонимной функции () => {} в обратном вызове onClick)

компоненты/UpdateQuantity.js

import React, { Component, Fragment } from "react";
import Button from "./button";

export default class App extends Component {
  state = {
    items: [
      { id: "Apples", quantity: 0 },
      { id: "Strawberries", quantity: 0 },
      { id: "Grapes", quantity: 0 },
      { id: "Apricots", quantity: 0 }
    ]
  };

  handleChangeEvent = (id, val) => {
    this.setState(prevState => ({
      items: prevState.items.map(item => {
        const nextVal = item.quantity + val;

        return id === item.id
          ? { id, quantity: nextVal > 0 ? nextVal : 0 }
          : { ...item };
      })
    }));
  };

  render = () => (
    <div className = "container">
      <h1>Updating Values Inside Array</h1>
      {this.state.items.map(props => (
        <div key = {props.id} className = "container">
          <div>
            {props.id} ({props.quantity})
          </div>
          <Button
            {...props}
            className = "uk-button uk-button-primary"
            handleChangeEvent = {this.handleChangeEvent}
            value = {1}
          >
            +
          </Button>
          <Button
            {...props}
            disabled = {props.quantity === 0}
            className = "uk-button uk-button-danger"
            handleChangeEvent = {this.handleChangeEvent}
            value = {-1}
          >
            -
          </Button>
        </div>
      ))}
    </div>
  );
}

компоненты /button.js

import React, { PureComponent } from "react";
import PropTypes from "prop-types";

export default class Button extends PureComponent {
  static propTypes = {
    children: PropTypes.string.isRequired,
    className: PropTypes.string,
    disabled: PropTypes.bool,
    id: PropTypes.string.isRequired,
    handleChangeEvent: PropTypes.func.isRequired,
    value: PropTypes.number.isRequired
  };

  handleClick = () => {
    this.props.handleChangeEvent(this.props.id, this.props.value);
  };

  render = () => (
    <button
      disabled = {this.props.disabled || false}
      className = {this.props.className}
      onClick = {this.handleClick}
      style = {{ marginRight: 10 }}
    >
      {this.props.children}
    </button>
  );
}

Обновленный ответ и объяснение. Кроме того, я включил альтернативное решение для принятого ответа, которое позволяет избежать привязки в методе рендеринга.

Matt Carlotta 23.02.2019 16:33
Ответ принят как подходящий

Я знаю, что ответ был принят, но на самом деле он не полностью удовлетворяет требованиям, т.е.

Each callback should take a single, integer value as a parameter which is the amount to increment the counter's existing value by.

Принятый ответ принимает объект события в качестве параметра, который не является указанным требованием. Единственный способ строго удовлетворить ожидаемое требование — привязать уникальный

"...onIncrement callbacks as props..."

для каждого счетчика. Этот подход имеет недостатки и влияет на производительность как описано в этой статье

Рабочий пример наhttps://codesandbox.io/s/j2vxj620z9

Да, согласен с тобой. Я пробовал решение сегодня и думал о том же! :)

Spaniard 19.02.2019 15:52

Другие вопросы по теме