I need to change the state of sibling components in my React App.
I use state and setstate
I need to change the state of sibling components. When loading the page, there must exist (visible in the page) <BkUser /> and when clicking "button id =" ds-visual "it must be deleted (<BkUser /> mustn't exist) and there must exist <BkDescanso />.
When you click on <BkSleep /> (in the div parent) you should remove <BkDescanso /> and show <BkUser />
This is the web.
There should never be <BkUser/> and <BkSleep> at the same time. <Bkuser /> is the blue block and <BkDescanso /> is the red block
This is my code:
Edit: I edit my original code because I fix the problem. This is the final OK Code. In the end the most important thing was the state conditional
{
this.state.usuario ? (<BkUser handleClick = {this.handleClick} usuario={this.state.usuario}/>): (<BkDescanso handleClick = {this.handleClick} usuario={this.state.usuario}/>)}
import React, { Component } from 'react';
class Header extends Component {
constructor(props) {
super(props);
this.state = {
usuario: true,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
usuario: !state.usuario
}));
//alert("Works button");
}
render(){
return (
<header className="header">
<div className="configuracion">
{
this.state.usuario
? (
<BkUser handleClick = {this.handleClick} usuario={this.state.usuario}/>
)
: (
<BkDescanso handleClick = {this.handleClick} usuario={this.state.usuario}/>
)}
<div className="content-btn">
<button id="config" className='btn btn--rounded'><span className="ico-configuracion"></span></button>
<button id="salir" className='btn btn--rounded'><span className="ico-exit"></span></button>
</div>
</div>
</header>
);
}
}
class BkUser extends Component{
render(){
return ((
<div className='usuario'>
<img src="../img//usuario.svg" alt="Imagen usuario"/>
<div className="content-usuario">
<span id="nm-usuario" className="h4">Hermione Jane Granger</span>
<span id="tp-usuario" className="h5">Supervisor</span>
</div>
<div className="content-descansos">
<div className="botones">
<button id="ds-visual" className='btn btn--rounded' onClick={this.props.handleClick}><span className="ico-visual"></span></button>
<button id="ds-admin" className='btn btn--rounded'><span className="ico-tiempo-administrativo"></span></button>
<button id="ds-otros" className='btn btn--rounded'><span className="ico-descanso"></span></button>
</div>
<div className="ds-actual">
<span id="ds-tipo">Administrativo</span>
<span id="ds-tiempo">00:08:47</span>
</div>
</div>
</div>
));
}
}
class BkDescanso extends Component {
render(){
return ((
<div className='usuario descanso' onClick={this.props.handleClick}>
<h3>Finalizar descanso</h3>
</div>
));
}
}
export default Header;
Right now handleClick works but always exist BkUser and BkDescanso. I need only one to exist. If you click on id = "ds-visual" the bkUser block should disappear and BkDescanso appear. Then if you click on div className = 'user rest' in BkUser there should only be BkDescanso.
I think that it is not able to know when it is true and when it is false to show or hide
Thanks a lot for the help.
You're missing two things:
First you have to pass the handleClick function to the BkUser component, and then you have to call it via this.props.handleClick.
...
<BkUser handleClick={this.handleClick} usuario={this.state.usuario} />
....
<button
id="ds-visual"
className="btn btn--rounded"
onClick={this.props.handleClick}
>
ds-visual
<span className="ico-visual" />
</button>
CodeSandbox here.
Read more here.
You can change the state of the siblings by passing a function from the parent via props into them.
In the end your siblings are the children of their parent.
You can read this articles on how to change the state of child components.
React js change child component's state from parent component
https://medium.freecodecamp.org/react-changing-state-of-child-component-from-parent-8ab547436271
An other thing you could look into would be React Redux.
Related
Hello, I don't know how to change component when clicking on the button on the left side, so it'll render the component into the right side, but only the one that I want, not all of it, I know how to clicking component, it'll transfer me into another route using Switch, etc but this doesn't seem like the case. Can someone help me out?
I can't figure out how to keep the button at the left side stay while the right side component is changing
Here are my code:
import { Component } from "react";
import UserManagement from "../UserManagement/index";
import RoleManagement from "../RoleManagement/index";
import QuizManagement from "../QuizManagement/index";
class Admin extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div class="admin-section">
<div class="admin-control col-25">
/- 3 buttons for 3 components I want to render
<button>
<label>User management</label>
</button>
<button>
<label>Role management</label>
</button>
<button>
<label>Quiz management</label>
</button>
</div>
<div class="admin-modify col-75">
/- When I click button the component will display here, I just don't know how to parse it into here but only one, if it like this it'll render all 3 of it.
<UserManagement />
<RoleManagement />
<QuizManagement />
</div>
</div>
);
}
}
export default Admin;
A simple solution would be:
Step 1 : Add a state called management in this.state = {}. Like
constructor(props) {
super(props);
// you can default this to any of the three
this.state = {management: "user"};
}
Step 2: Add onClick handler on the buttons like
<button onClick={() => {this.setState({management: "user"}) }}>
<label>User management</label>
</button>
<button onClick={() => {this.setState({management: "role"}) }}>
<label>Role management</label>
</button>
<button onClick={() => {this.setState({management: "quiz"}) }}>
<label>Quize management</label>
</button>
Step 3: Conditionally render your tables based on the state.
{this.state.managment === "user" && <UserManagement />}
{this.state.managment === "role" && <RoleManagement />}
{this.state.managment === "quiz" && <QuizManagement />}
Step 4: Turn up :).
Have you tried to simply hide/show the right component when listening to the user's "onClick" event on the left component?
I am absolutely new to React and need to create a button to take the user from a current component (MovieView) to the main one (MainView). I managed to create it properly and I used onClick to trigger the display of MainView. I know I cannot call a class from a function (so as console says), so I created a second function to be called and trigger MainView. But as you may wonder, it does not work. This is the code I am using:
import React from 'react';
import { MainView } from '../main-view/main-view';
function NewButton() {
return <MainView />;
}
export class MovieView extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
const { movie } = this.props;
if (!movie) return null;
return (
<div className="movie-view">
<img className="movie-poster" src={movie.imagePath} />
<div className="movie-title">
<span className="label">Title: </span>
<span className="value">{movie.title}</span>
</div>
<div className="movie-description">
<span className="label">Description: </span>
<span className="value">{movie.plot}</span>
</div>
<div className="movie-genre">
<span className="label">Genre: </span>
<span className="value">{movie.genre.name}</span>
</div>
<div className="movie-director">
<span className="label">Director: </span>
<span className="value">{movie.director.name}</span>
</div>
<div className="back-movies">
<button onClick={NewButton}>Back</button>
</div>
</div>
);
}
}
Could you any seeing this to point the path to take or how I can call this MainView class properly. I understand it's a simple task, but I am afraid I still haven't absorbed React principles yet. Thanks in advance to you all.
Without introducing additional dependencies, probably the easiest way for this example is to use state to track if the button has been clicked. If it has been clicked, render MovieView, if not render MainView.
For this you need to following:
Set state variable that tracks that MainView should be rendered in onClick event. (boolean will probably suffice)
in render():
if the state var is false, render the content of MovieView.
if the state var is true, render the MainView component.
Implementation details are left as an exercise :)
If you're using a router already (like react-router) you could probably just update the url to an url that is linked to the MainView component..
I make the Class into a Function Component instead, and implement it like this :-)
import React from "react";
import { MainView } from '../main-view/main-view';
export const MovieView = ({ movie }) => {
const { showMainView, setShowMainView } = React.useState(false);
if (!!movie) {
return null;
}
function toggleMainView() {
setShowMainView(true);
}
return (
<React.Fragment>
{ showMainView ? (
<MainView />
) : (
<div className="movie-view">
<img className="movie-poster" src={movie.imagePath} />
<div className="movie-title">
<span className="label">Title: </span>
<span className="value">{movie.title}</span>
</div>
<div className="movie-description">
<span className="label">Description: </span>
<span className="value">{movie.plot}</span>
</div>
<div className="movie-genre">
<span className="label">Genre: </span>
<span className="value">{movie.genre.name}</span>
</div>
<div className="movie-director">
<span className="label">Director: </span>
<span className="value">{movie.director.name}</span>
</div>
<div className="back-movies">
<button onClick={() => toggleMainView()}>Back</button>
</div>
</div>
)}
</React.Fragment>
);
};
export default MovieView;
If you need to add or remove components or show a new view based on an user action in react we need to do this via state .
import React from 'react';
import { MainView } from '../main-view/main-view';
export class MovieView extends React.Component {
constructor() {
super();
this.state = {
showMainView: false;
};
}
toggleMainView = () => this.setState(prevState => ({ showMainView:
!prevState.showMainView}))
render() {
const { movie } = this.props;
const { showMainView } = this.state;
if (!movie) return null;
return (
<div className="movie-view">
<img className="movie-poster" src={movie.imagePath} />
<div className="movie-title">
<span className="label">Title: </span>
<span className="value">{movie.title}</span>
</div>
<div className="movie-description">
<span className="label">Description: </span>
<span className="value">{movie.plot}</span>
</div>
<div className="movie-genre">
<span className="label">Genre: </span>
<span className="value">{movie.genre.name}</span>
</div>
<div className="movie-director">
<span className="label">Director: </span>
<span className="value">{movie.director.name}</span>
</div>
<div className="back-movies">
<button onClick={this.toggleMainView}>Back</button>
</div>
// render the mainView based on the state
{ showMainView && <MainView />}
</div>
);
}
}
I reached a solution that I'd not do with the help I had here. I have first pasted here some pieces of code but, I believe it can be helpful to give the whole answer.
So, the question I placed here was regarding a component called <MovieView, that was a part of an app that interacted with another component (in another file) called MainView.
The solution was the insertion of the method onClick on the live 14 of <MovieView> and the button on line 39 that you can see at this file on Github. If the file was updated, check the version for November 22nd.
The solution had, however, been found in the main component, <MainView>, adding code on lines 48 and 49. Here is the link to the file: https://github.com/cgobbet/react-client/blob/master/src/components/main-view/main-view.jsx
The repository with the whole app is on this page: https://github.com/cgobbet/react-client.
And sorry for the reviewers (I had accidentally deleted part of the fantastic answer submitted by #nick-miller.
So I have a component, called AddButton
export default class AddButton extends React.Component {
constructor(props) {
super(props);
}
addItem(e) {
this.btn.setAttribute('disabled', 'disabled');
this.props.addItem(e.target.getAttribute('data-row-index'))
}
render() {
return (
<div className="row">
<div className="col-md-12 text-center">
<button ref={btn => {this.btn = btn }} className="btn btn-success" onClick={this.addItem.bind(this)} data-row-index={this.props.rowIndex}>Add</button>
</div>
</div>
)
}
}
Some where else in the code I do:
if (this.props.addButton) {
rows.push(
<td key="add">
<AddButton
addItem={this.props.addItem}
rowIndex={this.props.rowIndex}
/>
</td>
)
}
So I have at one time 50 of these in a table at the end of the row. When one is clicked I wanted to disable all the buttons.
So as you can see I have done, in addItem(e):
addItem(e) {
this.btn.setAttribute('disabled', 'disabled');
this.props.addItem(e.target.getAttribute('data-row-index'))
}
But when I test this, only the button after the one is clicked is disabled. I want them all to be disabled
Any way I could modify this to achieve that?
React components have syntax like HTML DOM but they are not, they are modules and every time you use that module for example in your table it would be a new instance of that module. So if you want to share a state between them you have to pass it as props to them and you shouldn't treat them as HTML nodes.
I am new to React and am trying to add an onClick event handle to a list item which is created using map.
Currently, the page renders with the individual items each having their own button. However, when i click on any of the button an error
TypeError: Cannot read property 'AddThisToReadingList' of undefined.
Any help would be appreciated in understanding what is happening. Thanks in advance.
import React, {Component} from 'react';
class ResultsList extends Component{
constructor(){
super();
this.state={
currentUserEmail: 'brad.ga.co',
currentBookId: {}
}
}
AddThisToReadingList(index){
console.log('this');
//set state
//axios request to get book info
//send to node update user
}
render(){
console.log('Rendering');
return(
<div>
<ul>
<ListedBooks data ={this.props.listFromGoogle}/>
</ul>
</div>
)
}
};
const ListedBooks= props =>{
if (props.data === undefined){
return (<div>Loading....</div>)
}
return(
props.data.map( (book, index)=>{
return <li key={index}>
<h4>Title: {book.volumeInfo.title}</h4>
<div>Author: {book.volumeInfo.authors.map((element, index)=> <p key={index}>{element}</p>)}</div>
<p>Description: {book.volumeInfo.description}</p>
<p>Release Date: {book.volumeInfo.publishedDate}</p>
<img src={book.volumeInfo.imageLinks.smallThumbnail}/>
<br/>
<button onClick={()=>this.AddThisToReadingList(index)}>Add to my reading list</button>
</li>
})
)
}
export default ResultsList;
This should do the trick:
In ResultsList:
<ListedBooks
data={this.props.listFromGoogle}
AddThisToReadingList={this.AddThisToReadingList.bind(this)}
/>
In ListedBooks:
<button onClick={()=>props.AddThisToReadingList(index)}>
Add to my reading list
</button>
ListedBooks knows nothing about this in ResultsList. this is, in fact, undefined in ListedBooks. You need to pass the method to ListedBooks as a prop in order to access it there.
Add additional data to button like
<button id="additem" data-index={index}></button>
Then you can add listener with native javascript
render() {
const button = document.getElementById('additem');
button.addEventListener('click', () => {
this.AddThisToReadingList(button.dataset.index);
})
return (
props.data.map( (book, index)=>{
return <li key={index}>
<h4>Title: {book.volumeInfo.title}</h4>
<div>Author: {book.volumeInfo.authors.map((element, index)=> <p key={index}>{element}</p>)}</div>
<p>Description: {book.volumeInfo.description}</p>
<p>Release Date: {book.volumeInfo.publishedDate}</p>
<img src={book.volumeInfo.imageLinks.smallThumbnail}/>
<br/>
<button data-index={index}>Add to my reading list</button>
</li>
})
)
}
I'm creating a component which has array of items and showing them on one page using map function.
Need help on how I can show a detail view of a item by updating route value dynamically based on the item I clicked.
Currently when I click on a item know more though the url change to some wrong value I don't see any change in the page.
Component:
{location[area].map((item, index) =>
<div key={index} className="col-md-4 mt-3">
<div className="card">
<div className="card-body">
<h5 className="card-title">{item.title}</h5>
<p className="card-text">{item.description}</p>
<Link to={'/view/${item.title}'} onClick={() => addDetail(item)} className="btn btn-primary">Know more</Link>
</div>
</div>
</div>
)}
Router:
<Route path='/view/:id' exact component={DetailPage} />
DetailPage Component:
class DetailPage extends Component {
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}
Result Anchor Tag:
<a class="btn btn-primary" href="/view/${item.title}">Know more</a>
Onclick result url:
http://localhost:3000/view/$%7Bitem.title%7D
You need to use backticks for the to prop of your Link components so that template literals will be used and the variable will be inserted into the string.
<Link
to={`/view/${item.title}`}
onClick={() => addDetail(item)}
className="btn btn-primary"
>
Know more
</Link>
Well,As #tholle suggested use template literal.Your route seems fine, just make use of react life cycle method componentWillReceiveProps in the Detail Page Component.Here is the code
class DetailPage extends Component {
componentWillReceiveProps(nextProps){
if(this.props.match.params.id !== nextProps.match.params.id){
//make a call with updated nextProps.match.id or filter the existing data store
with nextProps.match.id
}
}
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}