React component re-renders endlessly with onClick - javascript

I want the onClick event of the button in result.js to render my Spinner component, and have so far (kind of) gotten it to do so. At the moment, Spinner mostly has some console.log() statements, and it keeps logging "Rendered spinner." endlessly after clicking the button, about once every second.
For the record, the returned paragraph isn't being displayed, but I haven't gotten around to debugging that yet. Also, I have excluded some code in Result.js that I think is irrelevant.
For now, I just want Spinner to only render once after pressing the button. Any tips?
result.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import Spinner from "./spinner";
class UnboxResult extends Component {
constructor(props) {
super(props);
this.state = {
showSpinner: false
};
this.handleUnboxClicked = this.handleUnboxClicked.bind(this);
}
handleUnboxClicked(event) {
event.preventDefault();
console.log("Inside handleUnboxClicked");
this.setState({
showSpinner: true
});
}
render() {
return (
<section className="opening">
<div className="container">
<div className="row">
<button onClick={this.handleUnboxClicked}>UNBOX</button>
</div>
<div className="row">
{this.state.showSpinner ?
<Spinner items={this.props.unbox.items}/> :
null}
</div>
</div>
</section>
);
}
}
export default connect(state => ({
unbox: state.unbox
}))(UnboxResult);
spinner.js:
import React, { Component } from 'react';
class Spinner extends Component {
constructor(props) {
console.log("Before super");
super(props);
console.log("Ran constructor.");
}
render(){
console.log("Rendered spinner.");
return(
<p>Spinning..</p>
);
}
}
export default Spinner;

You could add a handler method to update the state from spinner
handleClick(){
this.setState({
showSpinner: true
})
}
and in your render it will need to be passed as prop
<div className="row">
{this.state.showSpinner ?
<Spinner handleClick={this.handleClick}/> :
null}
</div>
In your spinner component return you can trigger this using onclick
<button onClick = {this.props.handleClick} > Click </button>
This will allow you to update the state back in your parent, You might want to figure out how you would display the items one at a time in spinner and only set state to false when there is no items left to display.
Sorry if i misunderstood your comment.

Related

how to call functional component in class based component using onClick event?

i want to show my functional component in class base component but it is not working. i made simpletable component which is function based and it is showing only table with some values but i want to show it when i clicked on Show user button.
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.userList = this.userList.bind(this);
}
userList = () => {
//console.log('You just clicked a recipe name.');
<SimpleTable/>
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList} >
Show User List
</Button>
</div>
);
}
}
export default ShowUser;
Why your code is not working
SimpleTable has to be rendered, so you need to place it inside the render method. Anything that needs to be rendered inside your component has to be placed there
On Click can just contain SimpleTable, it should be used to change the value of the state variable that controls if or not your component will be shown. How do you expect this to work, you are not rendering the table.
Below is how your code should look like to accomplish what you want :
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.state = { showUserList : false }
this.userList = this.userList.bind(this);
}
showUserList = () => {
this.setState({ showUserList : true });
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.showUserList} >
Show User List
</Button>
{this.state.showUserList ? <SimpleTable/> : null}
</div>
);
}
}
export default ShowUser;
You can also add a hideUserList method for some other click.
Or even better a toggleUserList
this.setState({ showUserList : !this.state.showUserList});
If you're referring to the method userList then it appears that you're assuming there is an implicit return value. Because you're using curly braces you need to explicitly return from the function meaning:
const explicitReturn = () => { 134 };
explicitReturn(); <-- returns undefined
const implicitReturn = () => (134);
implicitReturn(); <-- returns 134
The problem lies with how you are trying to display the SimpleTable component. You are using it inside the userList function, but this is incorrect. Only use React elements inside the render method.
What you can do instead is use a state, to toggle the display of the component. Like this:
const SimpleTable = () => (
<p>SimpleTable</p>
);
class ShowUser extends React.Component {
constructor(props) {
super(props);
this.state = {showSimpleTable: false};
this.toggle= this.toggle.bind(this);
}
toggle = () => {
this.setState(prev => ({showSimpleTable: !prev.showSimpleTable}));
}
render() {
return (
<div>
<button variant = "contained" color = "primary" onClick={this.toggle}>
Show User List
</button>
{this.state.showSimpleTable && <SimpleTable />}
</div>
);
}
}
ReactDOM.render(<ShowUser />, document.getElementById("app"));
<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="app"></div>
The functionality you are looking for is called Conditional Rendering. The onClick prop function is an event handler and events in react may be used to change the state of a component. That state then may be used to render the components. In normal vanilla javascript or jQuery we call a function and modify the actual DOM to manipulate the UI. But React works with a virtual DOM. You can achieve the functionality you are looking for as follows:
class ShowUser extends Component {
constructor(props) {
super(props)
// This state will control whether the simple table renders or not
this.state = {
showTable: false
}
this.userList.bind(this)
}
// Now when this function is called it will set the state showTable to true
// Setting the state in react re-renders the component (calls the render method again)
userList() {
this.setState({ showTable: true })
}
render() {
const { showTable } = this.state
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList}>
Show User List
</Button>
{/* if showTable is true then <SimpleTable /> is rendered if falls nothing is rendered */}
{showTable && <SimpleTable />}
</div>
)
}
}

React Loading New Page

I am trying to load a different React component using a button. It worked when doing it for authentication with GitHub using Firebase, but won't work for this page.
import React from 'react';
import './index.css';
import GamePage from '../Game';
class Home extends React.Component {
constructor(props){
super(props);
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
return(
<div>
<GamePage />
</div>
)
}
render(){
return(
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}
export default Home;
Is there something wrong with my LoadGamePage function?
How it is supposed to work? You have an onclick handler, which calls a class method. That class method, called LoadGamePage, returns JSX. Okey, but what now? It is returned, but... not rendered. It won't display anywhere. What would I suggest you? Instead of returning the JSX inside that handler, I would set state and depending on state I would render the Game Page or not.
class Home extends React.Component {
constructor(props){
super(props);
this.state = {
gameVisible: false,
}
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
this.setState({ gameVisible: true });
}
render() {
if (this.state.gameVisible) {
return <GamePage />
}
return (
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}

ReactJs component structure - Form inside modal

I am using the react-bootstrap Modal, Form and Button.
Desiring the functionality of clicking the button should open the modal with a form inside it. After filling out the form, one clicks a button (on the modal) and it validates the form data and posts it through a REST API.
I got far enough to figure out that my component split should be as follows:
A button component, a modal component and a form component.
What would be the correct way to structure these components in terms of props/state and placing the functions for validating the data? I am having trouble in understanding the child/parent relationship and when it's applicable
Components:
App Component: This is going to be the top level component
Button Component (If its just a button can also be
just a button):
If this is just a button you can keep this has a just a button in App component, if you are willing to reuse this with some custom element place it in a component.
Modal component: This is going to hold your modal like header,body,footer
Form component: This is a component which will hold the form and its validations.
Component Tree:
App Component will contain a state like showModal, we need to have a handler to set this value and this handler gets triggered when the button is clicked.
import FormModal from './FormModal';
class App extends React.Component {
state = {
showModal : false
}
showModalHandler = (event) =>{
this.setState({showModal:true});
}
hideModalHandler = (event) =>{
this.setState({showModal:false});
}
render() {
return (
<div className="shopping-list">
<button type="button" onClick={this.showModalHandler}>Click Me!
</button>
</div>
<FormModal showModal={this.sate.showModal} hideModalHandler={this.hideModalHandler}></FormModal>
);
}
}
Form Modal:
import FormContent from './FormContent';
class FormModal extends React.Component {
render() {
const formContent = <FormContent></FormContent>;
const modal = this.props.showModal ? <div>{formContent}</div> : null;
return (
<div>
{modal}
</div>
);
}
}
export default FormModal;
Hope that helped!
For basic pseudo code
Main Component:
import Modal from './Modal'
class Super extends React.Component {
constructor(){
this.state={
modalShowToggle: false
}
}
ModalPopUpHandler=()=>{
this.setState({
modalShowToggle: !modalShowToggle
})
}
render(){
return(){
<div>
<Button title='ModalOpen' onClick='this.ModalPopUpHandler'> </Button>
<ModalComponent show={this.state.modalShowToggle}>
</div>
}
}
}
ModalPopUp component:
import FormComponent from 'FormComponent'
class ModalComponent extends React.Component {
constructor(props){
super(props)
this.state={
modalToggle: props.show
}
}
render(){
if(this.state.modalToggle){
return(
<div>
<div className='ModalContainer'>
<FormComponent />
</div>
</div>
)
} else {
</div>
}
}
}
Form Component:
import Button from './Button'
class FormComponent extends React.Component {
constructor(){
this.state={
submitButtonToggle: true,
username: ''
}
}
inputHandler=(e)=>{
if(e){
this.setState({
username: e.target.value
})
}
}
render(){
return(
<div>
<input type='text' value='this.state.username' id='username' onChange='inputHandler' />
<Button title='Submit' disabled={this.state.username.length > 0}> </Button>
</div>
)
}
}
Above are the basic superComponent which we have rendered in app/main entry file.
And form || Modal Component. are the child component.
So in modal component I have called the same Form-component.
Here in Form-component input type handler, submit button is disabled from state.. with input string length we are handling its validation.
I hope it works for you.

How do I reveal my button only when on a specific component?

I want to show the Logout button on the same row of the title but only when the user has made it to Home component.
In other words, I don't want to show the logout button at all times, especially when the user's at the login screen. I want it to show on the same row of the title only when they've logged in successfully and they're in Home
How would I achieve this? My head hurts from trying to make this work :(
Below's what I've tried so far, among other things.
import React, {Component} from 'react';
import classes from './Title.css';
import LogoutButton from '../../containers/LogoutButton/LogoutButton';
import Home from '../../components/Home/Home';
class Title extends Component {
constructor(props) {
super(props);
this.state = {
show: false,
showLogoutButton: true
};
}
showButton() {
this.setState({show: true});
if(this.state.show) {
return <LogoutButton/>;
} else {
return null;
}
}
render() {
return(
<div>
{ this.state.showLogoutButton ? this.showButton : null }
<h1 className={classes.Title}>Pick Ups</h1>
</div>
);
}
}
export default Title;
You can try something like below. You don't need to deal with function and modifying states.
You can simply do like below
import classes from './Title.css';
import LogoutButton from '../../containers/LogoutButton/LogoutButton';
import Home from '../../components/Home/Home';
class Title extends Component {
constructor(props) {
super(props);
this.state = {
showLogoutButton: this.props.authenticated
};
}
render() {
const { showLogoutButton } = this.state;
return(
<div className="row" style={{"display" :"flex"}}>
{ showLogoutButton && <LogoutButton/>}
<h1 className={classes.Title}>Pick Ups</h1>
</div>
);
}
}
export default Title;
Note: When you modify state using setState the state value will be updated only after render so you can't directly check immediately modifying the value.

ReactJS beginner state updated but rendering in child not

Bear with me beginner at ReactJS so I am testing stuff out.
I created a static page as a component and in that component I load another custom component.
The data is coming from an ajax call and I update the state but it doesn't update the child component's view.
The static page
// Test.JS
import React from 'react';
import Layout from '../../components/Layout';
import Article from '../../components/Article';
import s from './styles.css';
import axios from 'axios';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
article: null
};
}
componentWillMount(){
axios.get("https://gist.githubusercontent.com/koistya/a32919e847531320675764e7308b796a/raw/articles.json")
.then(function(result) {
var theArticle = result.data.filter((article) => article.title.split(' ').join('') === this.props.route.params.title.split(' ').join(''));
this.setState({
article: theArticle[0]
})
}.bind(this));
}
render() {
return (
<Layout className={s.content}>
<Article {...this.state.article} />
</Layout>
);
}
}
export default Test;
As you can see i update the state with an ajax call but the child is not updated. the console log in Article is showing a null object because first time it renders there is nothing inside. But after updating the state I expect it should pass it through to the childeren.
import React from 'react';
import Link from '../Link';
class Article extends React.Component{
componentDidMount() {
console.log(this.props);
window.componentHandler.upgradeElement(this.root);
}
componentWillUnmount() {
window.componentHandler.downgradeElements(this.root);
}
render() {
return (
<div className="demo-card-wide mdl-card mdl-shadow--2dp" ref={node => (this.root = node)}>
<div className="mdl-card__title">
<h2 className="mdl-card__title-text">{this.props.author}</h2>
</div>
<div className="mdl-card__supporting-text">
</div>
<div className="mdl-card__actions mdl-card--border">
</div>
<div className="mdl-card__menu">
<button className="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i className="material-icons">{this.props.title}</i>
</button>
</div>
</div>
);
}
}
export default Article;
So I have the following questions:
1) First of all am I going the right direction is this how it should be done ?
2) Is a constructer not better than willmount event ?
2) Why is it now not updating the child view ?
3) Should I use a prop or state in this case (in Test.js) (still not sure after reading allot about it)
I changed it to componentDidMount and now it updates the view.
I have no idea why it didnt work before!

Categories