How to dynamically change component's style? - javascript

To simplify the code, lets say I have to components (buttons). A parent and a child. When the parent is clicked, it sends property (number) to the child. This number helps select a specific color inside an array. Then, when I click on the child button, its color changes to the selected color. And that's where my problem is. I don't know how to update the component with the new color.
var arrayOfColors = ["#cd6155", "#af7ac5", "#5499c7", "#48c9b0", "#58d68d", "#f5b041", "#dc7633", "#EAECEE",
"#c0392b", "#9b59b6", "#2980b9", "#1abc9c", "#2ecc71", "#f39c12", "#d35400", "#D5D8DC",
"#a93226", "#884ea0", "#2471a3", "#17a589", "#28b463", "#d68910", "#ba4a00", "#ABB2B9",
"#922b21", "#76448a", "#1f618d", "#148f77", "#239b56", "#b9770e", "#a04000", "#808B96",
"#7b241c", "#633974", "#1a5276", "#117864", "#1d8348", "#9c640c", "#873600", "#566573",
"#641e16", "#512e5f", "#154360", "#0e6251", "#186a3b", "#7e5109", "#6e2c00", "#2C3E50"];
var color = null; //Initial global value that changes to a number selected from the parent component
Child component
class Square extends React.Component {
constructor(props){
super(props);
this.state = {value: null};
}
handleClick(){
this.setState({value: color, });
var myStyle = {background: "pink"};
myStyle.background = arrayOfColors[this.props.value];
boardColor = myStyle;
...................................................
This is where the update should happen (I suppose).
The button with style={boardColor}
...................................................
}
render() {
return (
<button className="square" onClick={() => this.handleClick()}>
{this.state.value}
</button>
);
}
}
I've see somewhere it was possible to change a component's color by changing its name. And then, in a Css file, have a different style for each name. The change occurs between two colors. I don't think that solution is adapted to my problem since I have more than 40 different colors

<!doctype html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<title>Example</title>
</head>
<body>
<div id="app">
</div>
<script type="text/babel">
const SquareParent = () => {
return <Square parentColorNumber={11}/>
}
const arrayOfColors = ["#cd6155", "#af7ac5", "#5499c7", "#48c9b0", "#58d68d", "#f5b041", "#dc7633", "#EAECEE",
"#c0392b", "#9b59b6", "#2980b9", "#1abc9c", "#2ecc71", "#f39c12", "#d35400", "#D5D8DC",
"#a93226", "#884ea0", "#2471a3", "#17a589", "#28b463", "#d68910", "#ba4a00", "#ABB2B9",
"#922b21", "#76448a", "#1f618d", "#148f77", "#239b56", "#b9770e", "#a04000", "#808B96",
"#7b241c", "#633974", "#1a5276", "#117864", "#1d8348", "#9c640c", "#873600", "#566573",
"#641e16", "#512e5f", "#154360", "#0e6251", "#186a3b", "#7e5109", "#6e2c00", "#2C3E50"];
class Square extends React.Component {
constructor(props){
super(props);
this.state = { backgroundColor: null };
}
handleClick = () => {
const backgroundColor = arrayOfColors[this.props.parentColorNumber - 1];
this.setState({ backgroundColor });
}
render() {
const { backgroundColor } = this.state;
return (
<div>
<button className="square" style={{ backgroundColor }} onClick={ () => this.handleClick() }>
Current color - {this.state.backgroundColor || 'none'}
</button>
</div>
);
}
}
ReactDOM.render(<SquareParent/>, document.getElementById('app'))
</script>
</body>
</html>
I added as a parentColorNumber: 11. You can change it as you want.
You have a parent component that sends the data to it's child component.

class Square extends React.Component {
constructor(props){
super(props);
this.state = {value: null,backgroundColor:null};
}
handleClick(){
this.setState({value: color, });
var bg = arrayOfColors[this.props.value];
this.setState({backgroundColor:bg});
}
render() {
const { backgroundColor } = this.state;
return (
<button className="square" style={{backgroundColor}} onClick={() => this.handleClick()}>
{this.state.value}
</button>
);
}
}

Related

React Click Counter: Updating State of just one element

This should be pretty simple, but I can't figure out how to do it.
I have a component with multiple buttons, each with a "count" value, set with state. When a user clicks, the count goes up.
Right now, when I click one of the buttons, both counters change. How can I make it so only the div that was clicked updates, using the same state?
Edit: I don't want to have different counts, as I'd like for this component to render buttons dynamically. What if I don't know how many buttons I'll have at first?
class Block extends React.Component {
state = {
count: 0
};
handleClick = e => {
const count = this.state.count;
this.setState({ count: count + 1 });
};
render() {
return (
<div>
<button className="block" onClick={this.handleClick}>
<div className="counter">{this.state.count}</div>
</button>
<button className="block" onClick={this.handleClick}>
<div className="counter">{this.state.count}</div>
</button>
</div>
);
}
}
This is more of an issue of learning how to think in react.
If you need to be able to reuse a piece of functionality like a counter, you can make it its own component and have it manage its own state. Then you can reuse it wherever you need.
Here's an example:
class Counter extends React.Component {
state = {
count: 0
};
handleClick = () => {
// Use updater function when new state is derived from old
this.setState(prev => ({ count: prev.count + 1 }));
};
render() {
return (
<button className="block" onClick={this.handleClick}>
<div className="counter">{this.state.count}</div>
</button>
);
}
}
// Now you can use it dynamically like this:
class Block extends React.Component {
render() {
return (
<div>
<div>There are 4 counter component instances that each manage their own state.</div>
{[1,2,3,4].map(v => <Counter />)}
</div>
);
}
}
ReactDOM.render(<Block />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
you should define two state and when press each button update the current state and you can render the current state in the dome like this
state = {
firstCount: 0,
secondCount: 0
}
and write your action (function) to handle update state like this
handleUpdateCount = stateName => {
this.setState({
[stateName]= this.state[stateName] + 1
})
}
then you should called this function like this =>
this.handleUpdateCount('firstCount')
If your buttons are dynamic you can set your state to be an array and update the relevant index
class Block extends React.Component {
state = [];
handleClick = index => {
this.setState(state => {
const newState = [...state]; //keep state immutable
!newState[index] && (newState[index] = 0)
newState[index]++
return newState
});
};
render() {
return (
<div>
{[1,2,3].map((value, index) => <button className="block" onClick={() => this.handleClick(index)}>
<div className="counter">{this.state[index]}</div>
</button>)}
</div>
);
}
}
You have to use another value to update function when new state is derived from old state (like increment)
import React, { Component } from 'react'
export class Ref3 extends Component {
constructor(props) {
super(props)
this.state = {
count:0
}
}
//use prevState to help you update the old value to a new one
clickHandler=()=>{
this.setState((prevState=>({
count:prevState.count+1
})))
}
render() {
return (
<div>
<button onClick={this.clickHandler}>Click To Count</button>
{this.state.count}
</div>
)
}
}
export default Ref3

Easy communication of image between siblings

I'm new to ReactJS and I would like to communicate between my components.
When I click an image in my "ChildA" I want to update the correct item image in my "ChildB" (type attribute in ChildA can only be "itemone", "itemtwo", "itemthree"
Here is what it looks like
Parent.js
export default class Parent extends Component {
render() {
return (
<div className="mainapp" id="app">
<ChildA/>
<ChildB/>
</div>
);
}
}
if (document.getElementById('page')) {
ReactDOM.render(<Builder />, document.getElementById('page'));
}
ChildA.js
render() {
return _.map(this.state.eq, ecu => {
return (
<img src="../images/misc/ec.png" type={ecu.type_eq} onClick={() => this.changeImage(ecu.img)}/>
);
});
}
ChildB.js
export default class CharacterForm extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ name: "itemone" image: "defaultone.png"},
{ name: "itemtwo" image: "defaulttwo.png"},
{ name: "itemthree" image: "defaultthree.png"},
]
};
}
render() {
return (
<div className="items-column">
{this.state.items.map(item => (<FrameCharacter key={item.name} item={item} />))}
</div>
);
}
}
I can retrieve the image on my onClick handler in my ChildA but I don't know how to give it to my ChildB. Any hints are welcomed, thanks you!
What you need is for Parent to pass an event handler down to ChildA which ChildA will call when one of the images is clicked. The event handler will call setState in Parent to update its state with the given value, and then Parent will pass the value down to ChildB in its render method.
You can see this working in the below example. Since I don't have any actual images to work with—and to keep it simple—I've used <button>s instead, but the principle is the same.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
clickedItem: 'none',
};
}
render() {
return (
<div>
<ChildA onClick={this.handleChildClick}/>
<ChildB clickedItem={this.state.clickedItem}/>
</div>
);
}
handleChildClick = clickedItem => {
this.setState({ clickedItem });
}
}
const items = ['item1', 'item2', 'item3'];
const ChildA = ({ onClick }) => (
<div>
{items.map(name => (
<button key={name} type="button" onClick={() => onClick(name)}>
{name}
</button>
))}
</div>
);
const ChildB = ({clickedItem}) => (
<p>Clicked item: {clickedItem}</p>
);
ReactDOM.render(<Parent/>, document.querySelector('div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.development.js"></script>
<div></div>

Add class to siblings when Component clicked in React

I'm working on building my portfolio using React.js. In one section, I have four components laid out in a grid. What I want to do achieve is when one component is clicked, a css class is added to the siblings of this component so that their opacity is reduced and only the clicked component remains. In jQuery, it would be something like $('.component').on('click', function(){ $(this).siblings.addClass('fadeAway')}). How can I achieve this effect? Here is my code, thanks in advance for any and all help!
class Parent extends Component{
constructor(){
super();
this.state = {fadeAway: false}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
//Add class to siblings
}
render(){
const array = ["Hello", "Hi", "How's it going", "Good Times"]
return(
array.map(function(obj, index){
<Child text={obj} key={index} onClick={() => this.handleClick} />
})
)
}
}
A working example for this problem could look something like this, with a marginally more complex initialization array:
class Parent extends Component{
constructor(){
super();
this.state = {
elements: [
{
id: "hello",
text: "Hello",
reduced: false,
},
{
id: "hi",
text: "Hi",
reduced: false,
}
{
id: "howsItGoing"
text: "How's it going",
reduced: false,
}
{
id: "goodTimes",
text: "Good Times",
reduced: false,
}
],
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(e){
// copy elements from state
const elements = JSON.parse(JSON.stringify(elements));
const newElements = elements.map(element => {
if (element.id === e.target.id) {
element.reduced = false;
} else {
element.reduced = true;
}
});
this.setState({
elements: newElements,
});
}
render(){
return(
this.state.elements.map(function(obj, index){
<Child
id={obj.id}
text={obj.text}
reduced={obj.reduced}
key={index}
onClick={() => this.handleClick} />
});
);
}
}
Then you would just add a ternary, like so, to the Child component:
<Child
id={this.props.id}
className={this.props.reduced ? "reduced" : ""} />
This adds a bit more boilerplate than other examples, but it's extremely brittle to tie business logic to the text inside a component, and a stronger solution requires a stronger piece of identification, like an ID or class on the rendered DOM element. This solution also, if you so wish, easily allows you to expand your logic so that more than one element can remain at maximum opacity at once.
I would simply store in state index of selected item, and then pass fadeAway prop into Child component defined as
fadeAway={this.state.selectedIndex !== index}
After that you only need to set a fade-away class in Child based on this.prop.fadeAway and define necessary CSS rules.
Here is how it could look in your case:
class Parent extends React.Component{
constructor () {
super();
this.state = {selectedIndex: null}
}
handleClick (selectedIndex) {
this.setState({ selectedIndex })
}
render () {
const array = ["Hello", "Hi", "How's it going", "Good Times"]
return (
<div>
{array.map((obj, index) => {
const faded = this.state.selectedIndex && this.state.selectedIndex !== index
return <Child
text={obj}
fadeAway={faded}
key={index}
onClick={() => this.handleClick(index)} />
})}
</div>
)
}
}
class Child extends React.Component {
render () {
return (
<h2
onClick={this.props.onClick}
className={this.props.fadeAway ? 'fade-away' : ''}>
{this.props.text}
</h2>
)
}
}
ReactDOM.render(
<Parent />,
document.body
);
.fade-away {
opacity: 0.3;
}
<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>
You can achieve that using using a toggle variable :
handleClick(){
this.setState({fadeAway} => ({
fadeAway: ! fadeAway
)};
}
...
<Child
text={obj}
key={index}
onClick={() => this.handleClick}
className={this.state.fadeAway? 'class1' : 'class2'}/>
I perfer use state like currentWord to save the word was clicked in Parent component, presudo code is like below:
class Parent extends Component {
constructor(){
super();
this.state = {
fadeAway: false,
currentWord: ''
};
this.handleClick = this.handleClick.bind(this)
}
handleClick(currentWord){
this.setState({
currentWord: currentWord,
});
}
render(){
const array = ["Hello", "Hi", "How's it going", "Good Times"]
const currentWord = this.state.currentWord;
return(
array.map(function(obj, index){
<Child currentWord={currentWord} text={obj} key={index} onClick={() => this.handleClick} />
})
)
}
}
And in Child component
class Child extends Component {
// some other code
handleClick(e) {
this.props.handleClick(e.target.value);
}
render() {
const isSelected = this.props.text === this.props.currentWord;
// use isSelected to toggle className
<div
onClick={this.handleClick.bind(this)}
>{this.props.text}
</div>
}
}

How to get values/properties from a react-bootstrap checkbox?

I'm trying to use the react-bootstrap checkbox (https://react-bootstrap.github.io/components.html#forms-controls) and I need to fire an event when it changes state. It would also be great to be able to programatically un/check it and/or tell if it is checked. Unfortunately when the code is transpiled and rendered it wraps the input in a div.
How can I find this in the dom and manipulate it?
My code looks similar to this:
import React, { PropTypes } from 'react';
import { Checkbox } from 'react-bootstrap';
const EditItem = (props) => {
return (
<div>
<Checkbox style={{ marginLeft: '15px' }} >{props.itemLable}</Checkbox>
</div>
);
};
export default EditItem;
And the browser renders this:
...
<div class="checkbox" style="margin-left: 15px;">
<label>
<input type="checkbox">
</label>
</div>
...
I see the inputRef prop in the documentation but I can't find any examples of this or get it to work myself.
There are two ways: The React way and the not-so-React way.
The React way is to set the child component's state by passing it props and respond to changes in its state by attaching event handlers. In the case of Checkbox, that means setting the checked and onChange props.
Note in the below example how the parent component (App) keeps track of the Checkbox's state and can both set it with this.setState and query it with this.state.checkboxChecked.
const { Checkbox, Button } = ReactBootstrap;
class App extends React.Component {
constructor() {
super();
this.state = { checkboxChecked: false };
this.handleChange = this.handleChange.bind(this);
this.handleIsItChecked = this.handleIsItChecked.bind(this);
this.handleToggle = this.handleToggle.bind(this);
}
render() {
return (
<div>
<Checkbox
checked={this.state.checkboxChecked}
onChange={this.handleChange} />
<Button type="button" onClick={this.handleToggle}>Toggle</Button>
<Button type="button" onClick={this.handleIsItChecked}>Is it checked?</Button>
</div>
);
}
handleChange(evt) {
this.setState({ checkboxChecked: evt.target.checked });
}
handleIsItChecked() {
console.log(this.state.checkboxChecked ? 'Yes' : 'No');
}
handleToggle() {
this.setState({ checkboxChecked: !this.state.checkboxChecked });
}
}
ReactDOM.render(<App/>, document.querySelector('div'));
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.30.8/react-bootstrap.min.js"></script>
<div></div>
The not-so-React way is to get a reference to the rendered DOM element and access its checked property directly. I don't recommend this, because it necessarily pollutes your lovely functional React code with icky imperative code. Nevertheless, with React-Bootstrap you can do it by setting the inputRef prop, as in the below example:
const { Checkbox, Button } = ReactBootstrap;
class App extends React.Component {
constructor() {
super();
this.handleIsItChecked = this.handleIsItChecked.bind(this);
this.handleToggle = this.handleToggle.bind(this);
}
render() {
return (
<div>
<Checkbox
onChange={this.handleChange}
inputRef={ref => this.myCheckbox = ref} />
<Button type="button" onClick={this.handleToggle}>Toggle</Button>
<Button type="button" onClick={this.handleIsItChecked}>Is it checked?</Button>
</div>
);
}
handleIsItChecked() {
console.log(this.myCheckbox.checked ? 'Yes' : 'No');
}
handleToggle() {
this.myCheckbox.checked = !this.myCheckbox.checked;
}
}
ReactDOM.render(<App/>, document.querySelector('div'));
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.30.8/react-bootstrap.min.js"></script>
<div></div>
Thanks for the above answers. I generalized the above slightly for use when you have more than one checkbox in a given component:
constructor() {
super();
this.state = { YourInputName: false };
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
}
render() {
return (
<div>
<Checkbox
name="YourInputName"
onChange={this.handleCheckboxChange} />
</div>
);
}
handleCheckboxChange(event) {
const target = event.target
const checked = target.checked
const name = target.name
this.setState({
[name]: checked,
});
}
Have you tried setting an onChange property to your checkbox?
handleChange(event) {
this.setState(*set checkbox state here*);
}
<Checkbox onChange={this.handleChange}></Checkbox>

React not updating style

I'm trying to change style when a button is clicked using React. I can see value is changing when button is clicked, but style is not changing. I've been writing in many ways with no luck.
export const Paragraph = () => {
var state = 'none'
const changeState = () => {
state = state == 'none' ? 'inline-block' : 'none'
}
return (
<div>
<p style={{display: state}}</p>
</div>
)
}
Better way to set a class instead inline styles.
class Paragraph extends React.Component{
constructor(){
super();
this.state = {
isClicked: false
};
}
onClick(){
let condition = this.state.isClicked;
this.setState({isClicked: !condition})
}
render(){
return (
<div onClick={this.onClick.bind(this)}>
<p className={this.state.isClicked? "class_1" : "class_2"}></p>
</div>
);
}
}

Categories