I have this simple code below. When I press the Toggle Button the component Child should hide/show, but it's not.
Do I have to re-render something?
I don't want to switch in/out a CSS class, just toggle via a button click
import React, {Component} from 'react';
let active = true
const handleClick = () => {
active = !active
}
class Parent extends React.Component {
render() {
return (
<div>
<OtherComponent />
{active && <Child />}
<button type="button" onClick={handleClick}>
Toggle
</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
I am the child
</div>
)
}
}
class OtherComponent extends React.Component {
render() {
return (
<div>
I am the OtherComponent
</div>
)
}
}
You need to get or set it via state:
class Parent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
active: true,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
active: !this.state.active
});
}
render() {
return (
<div>
<OtherComponent />
{this.state.active && <Child />}
<button type="button" onClick={this.handleClick}>
Toggle
</button>
</div>
)
}
}
Note that with this approach you will re:render the entire parent component (as well as it's children).
Consider using another approach, when you are passing a prop to the child component and it will render itself with content based on this prop (it can render an empty div or something).
There are number of libraries that make this job easy for you, like react-collapse with animations and stuff.
You should only use state and props to manage your app state.
So instead try:
class Parent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
active: true
};
this.handleClick = this.handleClick.bind(this);
}
const handleClick = () => {
this.setState({active = !this.state.active});
}
render() {
return (
<div>
<OtherComponent />
{this.state.active && <Child />}
<button type="button" onClick={handleClick}>
Toggle
</button>
</div>
);
}
}
Alernatively, you could use forceUpdate() to force a re-render, but this is strongly discouraged:
const handleClick = () => {
active = !active;
this.forceUpdate();
}
Related
I'm new to react and getting trouble to achieve something very simple.
I have 3 boxes with initial black bg-color,
I need that whenever the user click on one of the boxes, just the color of selected box will change to white while the other elements stay at initial color, if the first box changed color and then we click on the second box, so the first box return to initial color and the second turn to white.
This is what I have done so far:
import React from 'react'
import { CardContainer, Title } from './business-item.styles';
import './business-item.style.scss';
class BusinessItem extends React.Component {
constructor(props) {
super(props);
this.state = {
isActive: false
};
this.changeColor = this.changeColor.bind(this);
}
changeColor() {
this.setState({ isActive: true });
}
render() {
const {isActive} = this.state;
return (
<CardContainer
className={isActive ? 'choosen' : 'not-choosen'}
onClick={this.changeColor}>
<Title>{this.props.title}</Title>
</CardContainer>
)
}
}
export default BusinessItem;
I'm trying to create this screens:
You want to lift up state. The buttons are not independent from each other; they need to be controlled all together by their parent component. Something like:
class Select extends React.Component {
constructor(props) {
super(props);
this.state = { selected: null };
}
render(){
return (
<div>
<Button
selected={this.state.selected === "Dog"}
onClick={() => this.setState({selected: "Dog"})}
>Dog</Button>
<Button
selected={this.state.selected === "Cat"}
onClick={() => this.setState({selected: "Cat"})}
>Cat</Button>
</div>
)
}
}
class Button extends React.Component {
render(){
const className = this.props.selected ? "selected" : "";
return (
<button
className={className}
onClick={this.props.onClick}
>{this.props.children}</button>
)
}
}
You could lift your state up to track active item clicked
const BusinessItemContainer = ({businessItems}) => {
const [activeIndex, setActiveIndex] = useState(null)
return <>
{
businessItems.map((title, index) => <BusinessItem key={item} index={index} title={title} onClick={setActiveIndex} activeIndex={activeIndex}/ >)
}
</>
}
Then in your component
const BusinessItem = ({index, activeIndex, title, onClick}) => {
return (
<CardContainer
className={activeIndex === index ? 'choosen' : 'not-choosen'}
onClick={()=> onClick(index)}>
<Title>{title}</Title>
</CardContainer>
)
}
I have a parent component that has a button. When that button is clicked, it should scroll to a grid in the child component, but it is not working. There hasn't been any errors. This is what I have in the parent component:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
someState: undefined,
};
this.ref_grid = React.createRef();
}
handleClick = () => {
this.setState({
someState: newState,
}, () =>
{
if (this.ref_grid.current !== null) {
this.ref_grid.current.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}
}
);
}
render() {
return (
<>
<Button
variant="contained"
color="secondary"
size="small"
onClick={() => this.handleClick()}
>Click me!</Button>
<ChildComponent
forwardRef={this.ref_grid}
/>
</>
);
}
}
In the child component I have the ref:
class ChildComponent extends Component {
constructor(props) {
super(props);
this.state = {
open: false
};
}
render() {
const {
classes
} = this.props;
return (
<Grid container spacing={3} ref={this.props.forwardRef}>
</Grid>
)
}
}
I am new to React, so I am not sure if this is the right approach. Would appreciate if anyone has any idea or example how to solve this.
Thank you in advance.
I am trying to implement in toggle button feature where when clicking on button willshowtext and clicking on button again willhidetext.
When i tried implement this i am stuck at displaying the text . I used the below for showing the text
import React, { Component } from "react";
export default class DisplayStats extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
console.log('Click happened');
<div>HELLO</div>
}
render() {
return (
<div className="container">
<h1>This is the stats.</h1>
<button onClick={this.handleClick}>Click Me</button>
</div>
)
}
}
With this i can see the console.log is created but i cant able to see the HELLO when i clicked on the button.
Am i missing anything here ?
Any help is appreciated
Thanks
You cannot return an element from an event handler and have it render like that.
You need to hide the text behind a flag and then toggle that flag.
First we create a flag in state. This defines if the toggle text should be displayed.
this.state = {
showText: false // Should the text be displayed?
};
Next we update the click handler to toggle that state flag.
this.setState((state) => ({
showText: !state.showText // Toggle showText
}))
Finally we conditionally render the toggle text. If showText is true, then render the text, if it is false do not render it.
{this.state.showText && <div>HELLO</div>}
Optional:
As pointed out by Mosè Raguzzini you do not need to bind your event handler.
this.handleClick = this.handleClick.bind(this); // This is not needed
handleClick = () => {} // because this is an arrow function
All together now:
import React, { Component } from "react";
export default class DisplayStats extends Component {
constructor(props) {
super(props);
this.state = {
showText: false // Should the text be displayed?
};
}
handleClick = () => {
console.log('Click happened');
this.setState((state) => ({
showText: !state.showText // Toggle showText
}))
}
render() {
return (
<div className="container">
<h1>This is the stats.</h1>
{this.state.showText && <div>HELLO</div>}
<button onClick={this.handleClick}>Click Me</button>
</div>
)
}
}
You should change state on toggle.
import React, { Component } from "react";
export default class DisplayStats extends Component {
state = {
isToggled : false
}
constructor(props) {
super(props);
}
handleClick = () => {
console.log('Click happened');
this.setState({isToggled : !this.state.isToggled});
}
render() {
return (
<div className="container">
<h1>This is the stats.</h1>
<button onClick={this.handleClick}>Click Me</button>
</div>
{(() => {
if(this.state.isToggled){
return <div>HELLO</div>
}
else{
return <div></div>
}
})()}
)
}
}
You do not need to use bind if you already use arrow functions, beside this, you have to learn how to manage state:
import React, { Component } from "react";
export default class DisplayStats extends Component {
constructor(props) {
super(props);
this.state = {
displayedText: '',
}
}
handleClick = () => {
console.log('Click happened');
this.setState({ displayedText: 'This is my text.'});
}
render() {
return (
<div className="container">
<h1>This is the stats. {this.state.displayedText}</h1>
<button onClick={this.handleClick}>Click Me</button>
</div>
)
}
}
To achieve this, you'll want to track state in your component to determine if the text should be displayed or not. The following code should achieve what you're after:
export default class DisplayStats extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
console.log('Click happened');
// When the button is clicked, the text display state is toggled
this.setState({ showText : !this.state.showText })
}
render() {
// Extract state to determine if the text should be shown
const showText = !!this.state.showText
return (
<div className="container">
{ /* Render div with text is showState is truthy /* }
{ showText && <div>HELLO</div> }
<h1>This is the stats.</h1>
<button onClick={this.handleClick}>Click Me</button>
</div>
)
}
}
That is not how react and other state based frameworks work. The idea is that the view should change when the state changes and only state can cause any change in the view. What you would need to do is on click of button, change the state which in turn will cause your view to update
import React, { Component } from "react";
export default class DisplayStats extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
visible: false
}
}
handleClick = () => {
this.setState({visible: !this.state.visible});
}
render() {
return (
<div className="container">
<h1>This is the stats.</h1>
<button onClick={this.handleClick}>Click Me</button>
{ this.state.visible ? <div>Hello</div> : '' }
</div>
)
}
}
I have a simple component who show element onClick:
class MyComponent extends React.Component {
state = {
isVisible : false
}
render() {
const { isVisble } = this.state
return(
<div>
{isVisble ?
<div onClick={() => this.setState({isVisble: false})}>Hide</div> :
<div onClick={() => this.setState({isVisble: true})}>Show</div>}
</div>
)
}
}
I use this component three times in other component :
class MySuperComponent extends React.Component {
render() {
return(
<div>
<MyComponent />
<MyComponent />
<MyComponent />
</div>
)}
}
I need to pass isVisible at false for all other component if one of have isVisible to true
How to do that ?
Thanks
You should have your component controlled, so move isVisble to props and and then assign it from MySuperComponent.
Also pass MyComponent a callback so it can inform the parent if it wants to change the state.
You'd want some data structure to store that states.
https://codepen.io/mazhuravlev/pen/qxRGzE
class MySuperComponent extends React.Component {
constructor(props) {
super(props);
this.state = {children: [true, true, true]};
this.toggle = this.toggle.bind(this);
}
render() {
return (
<div>
{this.state.children.map((v, i) => <MyComponent visible={v} toggle={() => this.toggle(i)}/>)}
</div>
)
}
toggle(index) {
this.setState({children: this.state.children.map((v, i) => i !== index)});
}
}
class MyComponent extends React.Component {
render() {
const text = this.props.visible ? 'visible' : 'hidden';
return (<div onClick={this.props.toggle}>{text}</div>);
}
}
React.render(<MySuperComponent/>, document.getElementById('app'));
You can check your code here, is this what you want.
example
Let's say that my parent component got two child component :
Parent
| Child1
| Child2
I'am getting an input from Child2 and I'am passing it to the Parent component (until now, I know how to do). But then I need to pass that input to Child1 to update it's state.
How can I do that?
Hope you can get the main idea - create a function in the Parent component that will change the value passed to the Child1. ReactJS: Why is passing the component initial state a prop an anti-pattern?
class Parent extends Component {
constructor(props){
super(props);
this.state = {
value: ""
}
}
changeValue(value){
this.setState({value});
}
render(){
return (
<div>
<Child1 value={this.state.value}/>
<Child2 changeValue={changeValue}/>
</div>
)
}
}
class Child2 extends Component {
constructor(props) {
super(props);
this.state={
input: ""
}
}
handleChange(e){
var {value} = e.target;
this.setState({
input: value
},() => this.props.changeValue(value));
}
render(){
return (
<div>
<input value={this.state.input} onChange={this.handleChange}/>
</div>
)
}
}
class Child1 extends Component {
constructor(props) {
super(props);
this.state={value:''}
}
componentWillReceiveProps(nextProps) {
this.setState({value: nextProps.value})
}
render(){
return (
<div>
{this.props.value}
</div>
)
}
}
You can have a function in your child component that updates the state based on the value sent from the parent component. And you can access a function of the child component form the parent component using refs
Example
Parent:
class Parent extends React.Component {
funcUpdateChild1 = () => {
this.child1.updateState('value here');
}
render() {
return (
<Child1 ref={(ip) => {this.child1 = ip}} />
<Child2 ref={(ip) => {this.child2 = ip}} />
)
}
}
Child1
class Child1 extends React.Component {
updateState = (value) => {
//use the value to set state here
}
render() {
return (
//child1 contents here
)
}
}
**Component parent **
import React from 'react';
import MM from './modall';
class App extends React.Component {
constructor() {
super();
this.state = {
naslov:'',
telo:''
};
this.setStateHandler = this.setStateHandler.bind(this);
this.postaviStanje = this.postaviStanje.bind(this);
this.Stanje = this.Stanje.bind(this);
}
setStateHandler() {
this.setState({ naslov: "Naslov Prvi u Modalu", telo:"Novo Prvo telo modala"});
};
postaviStanje(){
this.setState({naslov: " Novi drugi u Modalu", telo:"Novo drugo telo modala"});
};
Stanje(){
this.setState({naslov: " Novi treci u Modalu", telo:"Novo trece telo modala"});
};
render() {
return (
<div>
<button onClick = {this.setStateHandler} data-toggle="modal" data-target="#modal">SET STATE</button>
<button onClick = {this.postaviStanje} data-toggle="modal" data-target="#modal">SET STATE2</button>
<button onClick = {this.Stanje} data-toggle="modal" data-target="#modal">SET STATE3</button>
<MM telo={this.state.telo} naslov={this.state.naslov} />)
</div>
);
}
}
export default App;
Compnent child
/**
* Created by trika on 31-Jan-18.
*/
import React,{Component} from 'react';
class Modal extends Component{
constructor(props) {
super(props);
this.state = {
naslov:this.props.naslov,
telo: this.props.telo
};
}
render(){
return(
<div className="modal" id="modal" role="dialog">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h1 className="modal-title"><strong>{this.props.naslov}</strong></h1>
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-body">
<p>Modal body text goes here.</p>
<h2><strong>{this.props.telo}</strong></h2>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-primary">Save changes</button>
<button type="button" className="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
);
}
}
export default Modal;
Modern Solution with Hook:
1. Parent Component:
const Parent = ({}) => {
const [child2Data, setChild2Data] = useState(null);
return(
<view>
<Child1 child2Data={child2Data} />
<Child2 setChild2Data={setChild2Data}/>
</view>
)
}
1. Child2:
const Child2 = ({ setChild2Data }) => {
const [data, setData] = useState(null);
const _setData = (_data) => {
setData(_data)
setChild2Data(_data)
}
return(
<view onClick={() => _setData("Any Data")}>
</view>
)
}
1. Child1:
const Child1 = ({ child2Data }) => {
const [data, setData] = useState(null);
useEffect(() => {
setData(child2Data)
}, [child2Data])
}