Задать вопрос
@Inspy

Как вывести контекст нажатого компонента React?

Есть пример кода codepen.io/anon/pen/XpzbGM?editors=0011
Как можно сделать так чтоб когда я нажимал на блок с кнопкой например тот где new york, эти данные переносились в зеленый блок, и в зависимости от того в каком блоке нажали на кнопку город менялся, помогите пожалуйста :)
  • Вопрос задан
  • 913 просмотров
Подписаться 1 Оценить Комментировать
Решения вопроса 1
@juicyigor
const locations = [
  {
    city: "New York"
  },
  {
    city: "Moscow"
  }
];

const First = props => 
  <div className="first">
    <button onClick={props.handleClick}>
      click
    </button>
    <p>{props.city}</p>
  </div>;

const Second = props => 
  <div className="second">
    <span>
      {props.city && props.city}
    </span>
  </div>;

class TestComponent extends React.Component {
  constructor() {
    super();
    this.state = {
      selectedCity: false,
    };
  }
  
  handleClick = city => () => {
    this.setState({
      selectedCity: city,
    });
  };
  
  render() {
    return (
      <div>
        {locations.map((data, i) => 
          <First 
            city={data.city} 
            handleClick={this.handleClick(data.city)} 
          />
        )}
        <Second city={this.state.selectedCity} />
      </div>
    );
  }
}

ReactDOM.render(
<TestComponent />, 
document.querySelector('[data-role-id="content"]'));


Здесь есть небольшой недочет в том, что this.handleClick при перерисовке пересоздается, но думаю для вас это не очень критично
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
Хочу дополнить ответ juicyigor т.к. он содержит одну критическую (для производительности) ошибку:

<First 
  city={data.city} 
  handleClick={this.handleClick(data.city)} 
/>


handleClick = city => () => {
  this.setState({
    selectedCity: city,
  });
};


Суть в том, что мы при каждом рендере создаем новый коллбэк и передаем его как prop компоненту First. Во-первых: даже без реакта у вас могут возникнуть проблемы со сборщиком мусора, если вы достаточно часто делаете ререндер. Во-вторых: хорошей практикой для компонентов-представлений (глупых компонентов - dump components) является использование так называемого pure render (путем наследования класса компонента от React.PureComponent). Это дает нам возможность ререндерить (обновлять) компонент только в том случае, если props или state изменились. У Вас в компоненте First state не используется, значит, рендер компонента будет зависеть только от props, но при этом - при каждом рендере родительского компонента вы передаете в First новый экземпляр handleClick. Значит - компонент First будет каждый раз рендерится, даже если по сути - данные не поменялись. Это плохо и является антипаттерном. Предлагаю вот такой вариант:

class First extends React.PureComponent {
  handleClick = () => {
    const { onClick, city } = this.props

    return onClick(city)
  }

  render() {
    const { onClick, city } = this.props

    return (
      <div className="first">
        <button onClick={onClick && this.handleClick}>
          click
        </button>
        <p>{city}</p>
      </div>
    )
  }
}


В компонент First я добавил метод handleClick, который вызывает коллбэк onClick (который передан через props) и передает туда city. Таким образом мы избавились от пересоздания коллбэка в рендере родительского компонента. Также обратите внимание на определение этого метода:

handleClick = () => {

Такая запись подразумевает в себе автобиндинг: т.е. this у вас будет ссылаться именно на ваш элемент.

И небольшое замечание по названиям: handleSomething - это функция, которую вы передаете в свойство с названием onSomething. Пример:
<input onBlur={handleBlur} />

И исходя из предложенных изменений код родительского компонента будет выглядеть так:

class TestComponent extends React.PureComponent{
  constructor() {
    super();
    this.state = {
      selectedCity: false,
    };
  }
  
  handleClick = city => {
    this.setState({
      selectedCity: city,
    });
  };
  
  render() {
    return (
      <div>
        {locations.map((data, i) => 
          <First 
            city={data.city} 
            onClick={this.handleClick} 
          />
        )}
        <Second city={this.state.selectedCity} />
      </div>
    );
  }
}


PS саму логику я не трогал, просто хотел обратить Ваше внимание на ошибки.
Ответ написан
@tot_chelovek
JsDev
Для этого есть redux.
Ответ написан
maxfarseer
@maxfarseer
https://maxpfrontend.ru, обучаю реакту и компании
Codepen

Для изменяемых данных в зеленом блоке, можно использовать state компонента. Устанавливаете state через функцию, которую передаете в props ( везде называется handleClick, но для удобства можете переименовать). Чтобы передать 'city' можно придумать разные варианты, например, с помощью data-*атрибута у элемента, который будет доступен внутри e.target.dataset.*

Зачем в constructor'e - this.XXX = this.XXX.bind(this) - жду ответ от вас )
Ответ написан
@Aves
Вариант с data-city и делегированием события.
Если список локаций постоянный, можно создать его элементы заранее.
const locations = [
  {city: "New York"},
  {city: "Moscow"}
];

const First = ({city}) => (
  <div className="first">
    <button data-city={city}>
      click
    </button>
    <p>{city}</p>
  </div>
);

const Second = ({city}) => (
  <div className="second">
    <span>{city}</span>
  </div>
);

const list = locations.map((data, i) => <First city={data.city} />);

class TestComponent extends React.Component {
  state = {};
  handleClick = ({target}) => {
    const {city} = target.dataset;
    if (city) this.setState({city});
  };
  render() {
    return (
      <div onClick={this.handleClick}>
        {list}
        <Second city={this.state.city} />
      </div>
    );
  }
}

ReactDOM.render(
  <TestComponent />, 
  document.querySelector('[data-role-id="content"]')
);
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы