I
I
Inspy2017-01-27 18:19:11
JavaScript
Inspy, 2017-01-27 18:19:11

How to display the context of the clicked React component?

There is an example codepen.io/anon/pen/XpzbGM?editors=0011
How can I make it so that when I click on a block with a button, for example, the one where new york is, this data is transferred to the green block, and depending on which block was clicked on the button the city changed, please help :)

Answer the question

In order to leave comments, you need to log in

5 answer(s)
J
juicyigor, 2017-01-28
@Inspy

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"]'));

There is a small flaw here in that this.handleClick is recreated when redrawing, but I think this is not very critical for you

N
Nikita Gushchin, 2017-01-28
@iNikNik

I want to supplement juicyigor's answer . it contains one critical (for performance) error:

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

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

The bottom line is that with each render we create a newcallback and pass it as a prop to the First component. First, even without react, you might have problems with the garbage collector if you rerender often enough. Secondly, a good practice for view components (stupid components - dump components) is to use the so-called pure render (by inheriting the component class from React.PureComponent). This gives us the ability to re-render (update) the component only if the props or state has changed. Your First state is not used in your component, which means that the rendering of the component will depend only on props, but at the same time, with each rendering of the parent component, you pass a new instance of handleClick to First. This means that the First component will be rendered every time, even if, in fact, the data has not changed. It's bad andis an antipattern . I offer this option:
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>
    )
  }
}

In the First component, I added a handleClick method that calls the onClick callback (which is passed through props) and passes the city there. Thus, we got rid of the re-creation of the callback in the render of the parent component. Also pay attention to the definition of this method:
This entry implies autobinding: i.e. this will refer to your element.
And a little naming note: handleSomething is a function that you pass to a property called onSomething. Example:
And based on the proposed changes, the code of the parent component will look like this:
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 I did not touch the logic itself, I just wanted to draw your attention to the errors.

P
Pasha Egoros, 2017-01-27
@tot_chelovek

That's what redux is for.

M
Maxim, 2017-01-27
@maxfarseer

Codepen
For mutable data in the green block, you can use the component's state. You set the state through the function that you pass to props (it's called handleClick everywhere, but you can rename it for convenience). To pass 'city', you can come up with different options, for example, using an data-*attribute on an element that will be available inside e.target.dataset.*
Why in a constructor - this.XXX = this.XXX.bind(this) - I'm waiting for an answer from you)

A
Aves, 2017-01-28
@Aves

Variant with data-city and event delegation.
If the list of locations is permanent, you can create its elements in advance.

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"]')
);

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question