Blueshift Blueshift - 1 month ago 8
React JSX Question

React: Calling two event handlers onClick and changing style of child component

I have a parent class-based component

A
and a child functional component
B
. Inside
B
I map over a list of names and render them as
li
elements, which
onClick
call the
onLanguageUpdate
handler declared in the parent component, and what this handler does is update the state to reflect the selected name.

Question then:

I need to call a second event handler in the same
onClick
, this time to change the color of the name the user has clicked on. I added a new property to the state,
color
, to represent a
className
that I can then toggle with the
handleStyleColorChange
handler. But how do I get the
li
elements in the child component to update their className (or style) based on the result of this handler? If I was doing all of this inside component A's
render
method, I could do
style={language === this.state.selectedLanguage ? {color: 'red'} : null}
on the
li
and call it a day.

// Component A
import React, { Component } from 'react';
import B from './B';

class A extends Component {
constructor(props) {
super(props);

this.state = {
selectedLanguage: 'All',
color: 'lang-black-text'
};
}

handleUpdateLanguage = (language) => {
return this.setState({ selectedLanguage: language });
}

handleStyleColorChange = (language) => {
if (language === this.state.selectedLanguage) {
return this.setState({ color: 'lang-red-text' });
} else {
return this.setState({ color: 'lang-black-text' });
}
}

handleClick = (language) => {
this.handleUpdateLanguage(language);
this.handleStyleColorChange(language);
}

render() {

return (
<LanguageList onLanguageUpdate={this.handleClick} />
);
}
}

export default A;





// Component B
import React from 'react';

const B = (props) => {
const languages = ['English', 'Spanish', 'Japanese', 'Italian'];

const languageListFormatted = languages.map(language => {
return (
<li
key={language}
onClick={() => props.onLanguageUpdate(language)}>{language}
</li>
);
});

return (
<ul className="languages">{languageListFormatted}</ul>
);
}

export default B;

Answer Source

You can't manage the color from the parent comp, it needs to be done from the child comp. Then, send the selectedLanguage to the child and you are good.

class A extends React.Component {
  constructor(props) {
    super(props);

	  this.state = {
      selectedLanguage: 'All',
      color: 'lang-black-text'
    };
  }

  handleUpdateLanguage = (language) => {
    return this.setState({ selectedLanguage: language });
  }

  handleStyleColorChange = (language) => {
    if (language === this.state.selectedLanguage) {
      return this.setState({ color: 'lang-red-text' });
    } else {
      return this.setState({ color: 'lang-black-text' });
    }
  }

  handleClick = (language) => {
    this.handleUpdateLanguage(language);
    this.handleStyleColorChange(language);
  }

  render() {

    return (
      <B
        onLanguageUpdate={this.handleClick}
        selectedLanguage={this.state.selectedLanguage}
      />
    );
  }
}

const B = (props) => {
  const languages = ['English', 'Spanish', 'Japanese', 'Italian'];

  const languageListFormatted = languages.map(language => {
    return (
      <li
        key={language}
        style={props.selectedLanguage === language ? {background: 'yellow'} : {}}
        onClick={() => props.onLanguageUpdate(language)}>{language}
      </li>
    );
  });

  return (
    <ul className="languages">{languageListFormatted}</ul>
  );
}

ReactDOM.render(
  <A />,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app"></div>