React Handle interaction state between components - javascript

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

Related

Why isn't React re-rendering the page after the state is changed?

so I was working on a basic Todo app using React.js and I was wondering why the todo component does not automatically re-render once the state changed (the state contains the list of todos- so adding a new todo would update this array)? It is supposed to re-render the Header and the Todo component of the page with the updated array of todos passed in as props. Here is my code:
import React from 'react';
import './App.css';
class Header extends React.Component {
render() {
let numTodos = this.props.todos.length;
return <h1>{`You have ${numTodos} todos`}</h1>
}
}
class Todos extends React.Component {
render() {
return (
<ul>
{
this.props.todos.map((todo, index) => {
return (<Todo index={index} todo={todo} />)
})
}
</ul>
)
}
}
class Todo extends React.Component {
render() {
return <li key={this.props.index}>{this.props.todo}</li>
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.addnewTodo = this.addnewTodo.bind(this);
}
addnewTodo = () => {
let inputBox = document.getElementById("input-box");
if (inputBox.value === '') {
return;
}
this.props.handleAdd(inputBox.value);
}
render() {
return (
<div>
<input id="input-box" type="text"></input>
<button type="submit" onClick={this.addnewTodo}>Add</button>
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { todos: ['task 1', 'task 2', 'task 3']}
this.handleNewTodo = this.handleNewTodo.bind(this);
}
handleNewTodo(todo) {
let tempList = this.state.todos;
tempList.push(todo);
this.setState = { todos: tempList };
}
render() {
return (
<div>
<Header todos={this.state.todos} />
<Todos todos={this.state.todos} />
<Form todos={this.state.todos} handleAdd={this.handleNewTodo} />
</div>
)
}
}
You are not updating the state correctly.
You need to make a copy of the this.state.todos, add the new todo in the copied array and then call this.setState function
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
Notice that this.setState is a function
You're updating state incorrectly,
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
This is the correct syntax.

How to effect other element with onClick

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>
)
}

Updating Context from a Nested Component is not Working

I am new to react, I am trying to pass theme string and and toggleTheme function from parent to child using Context API in react.I am practicing example from React Doc with little modification https://reactjs.org/docs/context.html
my code is as following:
import React from 'react';
const ThemeContext = React.createContext({
theme: 'light',
toggleTheme: () => {}
})
class MouseTracker2 extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === 'dark'
? 'light'
: 'dark',
}));
};
this.state={
theme: 'dark',
toggleTheme: this.toggleTheme
}
}
render() {
return (
<div>
<ThemeContext.Provider value={this.state}>
<Abc />
</ThemeContext.Provider>
</div>
)
}
}
class Abc extends React.Component {
render() {
return (
<div>
<ThemeContext.Consumer>
{({theme,toggleTheme}) => {return(<Def theme={theme} onClick=
{toggleTheme} />)}}
</ThemeContext.Consumer>
</div>
)
}
}
class Def extends React.Component {
render() {
return (
<div>
<p>efgh</p>
<div>{this.props.theme}</div>
</div>
)
}
}
export default MouseTracker2
In above Code, Context is passing string from parent to child properly. However, it is not passing function from parent to child.
Thanks in Advance :)
The toggleTheme function is passed on to Def by the name onClick and hence this.props.toggleTheme is unavailable and can be accessed by this.props.onClick
class MouseTracker2 extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme: state.theme === "dark" ? "light" : "dark"
}));
};
this.state = {
theme: "dark",
toggleTheme: this.toggleTheme
};
}
render() {
return (
<div>
<ThemeContext.Provider value={this.state}>
<Abc />
</ThemeContext.Provider>
</div>
);
}
}
class Abc extends React.Component {
render() {
return (
<div>
<ThemeContext.Consumer>
{({ theme, toggleTheme }) => {
return <Def theme={theme} onClick={toggleTheme} />;
}}
</ThemeContext.Consumer>
</div>
);
}
}
class Def extends React.Component {
render() {
return (
<div>
<p>efgh</p>
<div>{this.props.theme}</div>
<button onClick={this.props.onClick}>Toggle</button>
</div>
);
}
}
Working Codesandbox

How can I change the fontweight of an item at click method - React

I have a react code below where I iterate over in an array of grocery list. I created a state change and a style variable that will change only one item at a time when clicked.
However it did not work. For some reason when I click on one item it turns all of them bold.
const App = () => (
<div><GroceryListItem /></div>
);
class GroceryListItem extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div><GroceryList groceryItems={['Cucumber', 'Kale']}/></div>
);
}
}
class GroceryList extends React.Component {
constructor(props){
super(props);
this.state = {
done: false
};
}
onClickItem(){
this.setState({
done: !this.state.done
});
}
render(){
var style = {
fontWeight: this.state.done ? 'bold' : 'normal'
};
return (
<ul>
{
this.props.groceryItems.map(item => <li style={style} onClick={this.onClickItem.bind(this)} key={item}>{item}</li>)
}
</ul>
);
}
}
Any idea why is this not working and how to fix it?
PS. Suggestions on how to improve my code is appreciated.
Store the css variable in state and change on onClick
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => (
<div><GroceryListItem /></div>
);
class GroceryListItem extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div><GroceryList groceryItems={['Cucumber', 'Kale']}/></div>
);
}
}
class GroceryList extends React.Component {
constructor(props){
super(props);
this.state = {
done: false,
style: "normal"
};
}
onClickItem(item){
let style = {
[item]: "bold"
}
this.setState({
done: !this.state.done,
style: style
},() => {});
}
render(){
return (
<ul>
{
this.props.groceryItems.map(item => {
{console.log(this.state.style[item],"this.state")}
return (<li style={{fontWeight: this.state.style[item] || "normal"}} id={item} onClick={this.onClickItem.bind(this,item)} key={item}>{item}</li>
)
})
}
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
You are changing the state onClick, and changing the style variable which is accessible to the complete render function. You just need to know which of the element is clicked and store it in your state. You can try the below method.
class GroceryList extends React.Component {
constructor(props){
super(props);
this.state = {
selected: null
};
}
onClickItem (item) {
this.setState({
selected: item
})
}
render(){
return (
<ul>
{
this.props.groceryItems.map(item => <li style={{'fontWeight': this.state.selected === item ? 'bold' : 'normal'}} onClick={this.onClickItem.bind(this, item)} key={item}>{item}</li>)
}
</ul>
)
}
}

React toggle component

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();
}

Categories