Add & Remove CSS classes with React - javascript

I am new to React. I have a few buttons in a button group:
<div className="btn-group">
<button className="btn btn-mini btn-default" onClick={() => this.changeDataType("type1")} >Button1</button>
<button className="btn btn-mini btn-default" onClick={() => this.changeDataType("type2")} >Button2</button>
<button className="btn btn-mini btn-default" onClick={() => this.changeDataType("type3")} >Button3</button>
</div>
Whenever the user clicks on one of the buttons, this button should become the active, selected one. I found out that I need to add the CSS class active to the corresponding button, but I am not sure how to implement this.
I thought about this for a bit. I have a changeDataType function connected to my buttons, in which I do some other stuff. Would I then, in there, somehow manipulate the CSS?
So I guess my questions are first, how to target the button I need to target, and second, how I could change that button's CSS with React.

In react when state changes react re-renders. In your case if you want to change the way something looks then you probably want to force another render. What you can do is have the className be part of state and then update the state when you want, causing the component to re-render with the new className. For example:
constructor() {
super();
this.state = {
className: 'btn'
}
}
render () {
return (
<Button className={this.state.className}>Click me</Button>
)
}
Since the className is bound to state updating state will cause the button to render again with the new className. This can be done like this:
updateClass() {
let className = this.state.className;
className = className + ' ' + 'btn-primary'
this.setState({className})
}
This is an example of the function you can call on the click of a button and update the className for the button.

There's a nice utility you can use for classname logic + joining classNames together
https://github.com/JedWatson/classnames
Based on setting React state for active you could do something like the following. You can get as complex as you need to with the logic. If the logic result is falsy, that key won't be included in the output.
var classNames = require('classnames');
var Button = React.createClass({
// ...
render () {
var btnClass = classNames({
btn: true,
'btn-active': this.state.isActive
});
return <button className={btnClass}>{this.props.label}</button>;
}
});

Here how I did this:
//ChatRoomPage component
function ChatRoomPage() {
const [showActionDropdown, setShowActionDropdown] = useState('hide');
function showActionDropdownHandler(){
console.log("clicked")
if(showActionDropdown=='hide')
setShowActionDropdown('show')
else
setShowActionDropdown('hide')
}
return (
<div>
<button onClick={ () => showActionDropdownHandler() } className="btn " type="button">Actions</button>
<div className={`action_menu ${showActionDropdown}`}>
...
</div>
</div>
);
}
export default ChatRoomPage;

Related

How to make the click on a search button filter the list of items in React.js

I am new to React.js. How do I make the search button list out the items in a list.Right now its doing nothing
<Button type = 'submit'>Search</Button>
You can use the onClick param for this, add it to the button then give it whatever function you want to run:
<button onClick={listItems}>Search</button>
Or
<button onClick={() => listItems()}>Search</button>
Or
<button onClick={() => {
console.log(listOfItems)
}}>Search</button>

React add html attribute to external component

I am using a component that I cannot change directly, but I would like to extend.
import { Button } from '#external-library'
// Currently how the button component is being used
<Button click={() => doSomething()} />
// I would like to add a tabIndex to the button
<Button click={() => doSomething()} tabIndex={0} />
I cannot add an attribute because the component is not expecting a tabIndex. I cannot directly modify the Button component.
How can I extend the <Button /> component so I can add attributes like tabIndex, etc?
I was hoping something like the following would work:
export default class ExtendedButton extends Button { }
// except I'm dealing with functional components
You can't edit custom component implementation without changing its internals.
// You can't add tabIndex to internal button without changing its implementation
const Button = () => <button>Click</button>;
In such cases, you implement a wrapper with desired props:
const Component = () => {
return (
<div tabIndex={0}>
<Button />
</div>
);
};
If the component forwarding ref (also depends to which element it forwarded in the implementation), you can use its attributes:
// Assumption that Button component forwards ref
const Button = React.forwardRef((props,ref) => <button ref={ref}>Click</button>);
<Button ref={myRef}/>
// Usage
myRef.current.tabIndex = 0;
You can access the inner DOM button element using React refs(read here)
most likely the external-lib you use provide a ref prop for the Button component which you use to pass your own create ref
const buttonRef = useRef(null);
<Button ref={buttonRef}/>
Then you can use buttonRef.current to add tabIndex when your data is ready to be populated in like
useEffect( () => {
if(buttonRef && buttonRef.current){
buttonRef.current.tabIndex = 2;
}
}, [props.someProperty] );

Toggle prop passed on one of many targets in ReactJS

Just starting off with ReactJS and have a project where I am showing an accordion of issues and including a details area that is hidden on the start.
There is a button in the accordion bar that should pass a prop to the child element to hide or show them. I have refs on the button and on the details child compoment and added a function to call the function and pass the ref of the details area. I am just not sure how to dynamically change the class hidden on one of many areas and not all of them.
Not sure if putting a class on each element and then learning how to toggle the particular child's class is better or changing the prop to the child.
I can get to the change function but am drawing a blank from there and all the googling shows how to do one element with a grand change of state but I need individual elements.
Here is what I have so far.
Parent
...
<AccordionItem key={item.id} className={iconClass} title={`${item.area}`} expanded={item === 1}>
{
item.issues.map(issue => {
let trim = (issue.issue.length>21) ? `${issue.issue.substring(0,22)}...`: issue.issue;
return (
<div className="issue-bar container-fluid">
<div className="row issue-bar-row">
<span className="issue-title"><img src={CriticalRed} alt="Critical"/> {trim}</span>
<span className="btns">
<button className="btn btn-details" onClick={() => this.showDetail(`details-${issue.id}`)}>Details</button>
</span>
</div>
<IssuesDetails ref={`details-${issue.id}`} issue={issue} shouldHide={true} />
</div>
)
})
}
<div>
</div>
</AccordionItem>
...
Child
export default class IssuesDetails extends Component{
render(){
let issueDetails = classNames( 'issue-details', { hidden: this.props.shouldHide } )
return(
<div className={issueDetails}>
<div className="issues-details-title">
<h3>{this.props.issue.issue}</h3>
</div>
<div className="issues-details-details">
{this.props.issue.details}
</div>
<div className="issues-details-gallery">
<ImageGallery source={this.props.issue.photos} showPlayButton={false} useBrowserFullscreen={false} />
</div>
<button className="btn btn-success">Resolve</button>
</div>
)
}
}
Thanks for any help you provide or places you can send me!
If i'm understanding correctly, you need to be able to swap out shouldHide={true} in certain circumstances. To do this, you'll want your parent component to have a state object which indicates whether they should be hidden or not.
Exactly what this state object looks like depends on what sort of data you're working with. If the issues is a single array, then perhaps the state could be an array of booleans indicating whether each issue is expanded or not. I suspect you may have a more nested data structure, but i can't tell exactly since some of the code was omitted.
So assuming you have an array, it might look like this (i've omitted some things from the render method for brevity):
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: (new Array(props.issues.length)).fill(false),
};
}
showDetail(index) {
let newHidden = this.state.hidden.slice();
newHidden[index] = true;
this.setState({
hidden: newHidden
});
}
render() {
return (
<AccordionItem>
{this.props.issues.map((issue, index) => {
<div>
<button onClick={() => this.showDetail(index))}/>
<IssuesDetails issue={issue} shouldHide={this.state.hidden[index]}/>
</div>
})}
</AccordionItem>
);
}
}
Take a look at these:
https://codepen.io/JanickFischr/pen/xWEZOG
style={{display: this.props.display}}
I think it will help with your problem. If you need more information, please just ask.

Select and change a specific element in ReactJS

I am trying to toggle the colours of two buttons in ReactJS. I can set the active state property of the selected button OK, but I can't work out how to change the style of another button (calcY) based on my selection (of calcX).
The code is brittle but I am pretty new to react and any pointers on best practices would be appreciated. PS also I am using react-bootstrap for the Form and buttons.
const MyForm = React.createClass({
handleChange(event, attribute) {
let eventValue = event.target.value;
if (attribute === 'calcX'){
this.setState({active: true});
this.setState({bsStyle: 'info'});
let calcXBtn = ReactDOM.findDOMNode(this.refs.calcBtnGroup.refs.calcX);
calcXBtn.setState({bsStyle: 'default'});
}
...
}
render() {
return (
<Form onSubmit={this.handleSubmit} horizontal>
<FormGroup>
<ButtonGroup ref="calcBtnGroup">
<Button active className='btn btn-info' ref="calcX" onClick={(event) => this.handleChange(event, 'calcX')}>Calculate X</Button>
<Button className='btn btn-default' ref="calcY" onClick={(event) => this.handleChange(event, 'calcY')}>Calculate Y</Button>
</ButtonGroup>
...
);
}
});
module.exports = MyForm;
You can set the className or style based of an element (or subcomponent) on the state of your component. It's nice to use a ternary operator and ES6 template literals here.
<Button ref="calcX" className=`btn ${this.state.active ? 'btn-info' : 'btn-default'}` onClick={(event) => this.handleChange(event, 'calcX')}>Calculate X</Button>
What this does, is setting a className based on the state of your component. The <Button> component always has a btn className. If state.active is true, the class btn-info will be added. Otherwise btn-default will be added.
So the only thing you have to do now, is set the state in your handleChange method and the classNames will be rendered appropriately.
Edit: it's not really necessary to use refs here. It's almost never necessary to use refs. You want use React events (onChange, onSubmit, etc.) to set input values on the state, and render those values in the value in your inputs. These are called controlled components. You can read more about it in the official documentation: https://facebook.github.io/react/docs/forms.html

reactjs - how to get data from a child component into onclick of a button passed in props?

I'm using ReactJS and ES2015
I pass a button via props down into a child component.
I can't see how to get data from the child into the onClick function of the button
Can anyone suggest what I need to do to get data from the child component into the onCLick function?
doDeleteItem = (blah) => {
console.log('the item to delete is: ', blah);
};
deleteButton = (
<button
className="btn btn-expand btn-stroke btn-success mr-5"
type="primary"
onClick={this.doDeleteItem}Delete item
</button>
)
render() {
return (
<TableContainer
deleteButton={this.deleteButton}
doDeleteItem={this.doDeleteItem}
/>
);
UPDATE: the comments say it's a bit unclear.
Here's the context:
The TableContainer component displays rows of data.
I push Button components down into the TableContainer via props.
The TableContainer renders the buttons.
I also push a function down for the Button to call in its onClick event.
The idea being that the user selects rows in the table, they push the button (such as delete for example) and the button runs its onClick function which deletes the selected rows.
The problem is that I can't see how to get the data that defines the selected rows into the onClick function.
It appears that the "scope" of the onClick function is actually the parent component, not the TableContainer component, so it cannot see the variables the define which data rows the user has chosen to delete.
Is that more clear? Let me know if not. thanks
Try to define doDeleteItem, deleteButton as methods instead of properties, and then call deleteButton in child and pass arguments what do you need
doDeleteItem(rows) {
console.log('the item to delete is: ', rows)
};
deleteButton(rows) {
return <button
className="btn btn-expand btn-stroke btn-success mr-5"
type="primary"
onClick={ () => this.doDeleteItem(rows) }
>
Delete item
</button>
}
render() {
return (
<TableContainer
deleteButton={ this.deleteButton }
doDeleteItem={this.doDeleteItem}
/>
);
}
Example
You should just pass down the parent function to the child as a property and separate the button component from the parent completely. You can include this button component in the child component (although it would be best to separate this component from child also) and insert the parent function there. If you want to pass parameters, you need to bind the function.
Parent:
doDeleteItem = (blah) => {
console.log('the item to delete is: ', blah);
};
render() {
const locale = this.props.app.locale;
return (
<TableContainer
onButtonClick={this.doDeleteItem.bind(this, blah)}
doDeleteTableItem={this.doDeleteTableItem}
/>
);
Child (TableContainer):
render() {
var deleteButton = (
<button
onClick={this.props.onButtonClick}
</button>
)
return (
// render TableContainer with deleteButton ..
);
As #WitVault mentioned in his comment you could pass some reference from the onClick in deleteButton so that the table knows which one to delete.
deleteButton = (
<button
className="btn btn-expand btn-stroke btn-success mr-5"
type="primary"
onClick={() => this.doDeleteItem(referenceToMeSoTableKnows)}>
Delete item
</button>
)
In your code deleteButton is getting called for just returing jsx. So you must be using it inside render method of TableContainer to render buttons.
and since here
<TableContainer
deleteButton={ this.deleteButton() }
doDeleteItem={this.doDeleteItem}
/>
doDeleteItem is passed as props inside TableContainer. you can access it via this.props inside TableContainer. Note this scope should be referring to scope of TableContainer so that you can access its props.
deleteButton = (
<button
className="btn btn-expand btn-stroke btn-success mr-5"
type="primary"
onClick={this.props.doDeleteItem.bind(this,item)}
</button>
)
I hope this should work.

Categories

Resources