React Help Needed - Components not updating when index increases - javascript

I cannot get the component displayed to update when the index increases. I am able to console the proper component now but because the onClick is below the component that needs to update, it isn't changing. Can someone help me fix my code? i think I am close but cannot figure it out for the life of me.
This sign up page is where I would like to update the component. Essentially I want to display each component in the array once the next button is clicked. Currently the function console logs everything as I want it to, it's just a matter of getting it to appear in the
it is returning an error "cannot read property 'count' of null":
import React from 'react';
import Q1Name from './questions/Q1Name';
import Q2Birthday from './questions/Q2Birthday';
import Q3City from './questions/Q3City';
import Q4YouReady from './questions/Q4YouReady';
import Q5Setting from './questions/Q5Setting';
import Q6Length from './questions/Q6Length';
import Q7Email from './questions/Q7Email';
class SignUpPage extends React.Component {
constructor(props) {
super(props);
this.state = {
i: 0
}
}
_handleClick() {
const components = [Q1Name, Q2Birthday, Q3City, Q4YouReady, Q5Setting, Q6Length, Q7Email];
if(this.state.i < components.length) this.setState({ i : this.state.i + 1});
}
// handleIncrement() {
// this.setState({ count: this.state.count + 1});
// }}
render() {
const components = [Q1Name, Q2Birthday, Q3City, Q4YouReady, Q5Setting, Q6Length, Q7Email];
const componentsToRender = components.map((Component, i) => (
<Component key={i} />
));
return (
<div className = "container-fluid signup-page">
<div className = "question-box">
{componentsToRender[this.state.i]}
<button type="submit" className="btn btn-custom btn-lg" onClick={() => this._handleClick}>Next Question!</button>
</div>
</div>
);
}
}
export default SignUpPage;
There are a few component types I am bringing in, age, birthday, email, and a few button clicks, etc.
import React from 'react';
class Q1Name extends React.Component {
handleSubmit(event) {
event.preventDefault();
this.props.onNext();
}
render() {
return (
<div className="questions q1" style={this.props.style}>
<h1 id="question-h1">What is your name?</h1>
<form>
<div className="form-group">
<input type="name" className="form-control text-form custom-form" id="nameInput" aria-describedby="name" placeholder="" />
</div>
{/* <button type="submit" className="btn btn-custom btn-lg" onSubmit={this.handleSubmit}>Next Question!</button> */}
</form>
</div>
);
}
}
export default Q1Name;
Here is an example of the button option component:
import React from 'react';
class Q5Setting extends React.Component {
render() {
return (
<div className="questions">
<h1 id="question-h1">What is your ideal setting?</h1>
<button type="button" className="btn btn-custom-select btn-lg">Take me to the beach!</button>
<button type="button" className="btn btn-custom-select btn-lg">Anywhere outdoors!</button>
<button type="button" className="btn btn-custom-select btn-lg">All about the city!</button>
</div>
);
}
}
export default Q5Setting;
Any help in figuring this out would be greatly appreciated!!

In your constructor initialise state
constructor(props) {
super(props)
this.state = { i: 0 }
}
Write helper method handleClick
_handleClick() {
if(this.state.i < components.length) this.setState({ i : this.state.i + 1});
}
Now reference componentsToRender using i in state
`componentsToRender[this.state.i]
Don't forget to call your helper function on click.
onClick = {() => this._handleClick()}
The idea is your app will only re-render when your state object changes. Follow that rule for your components you wish to re-erender on the fry.

Related

How to scroll to the top of a wizard form on page change

so I have a 3 page redux wizard form, whenever a new page is opened the view starts at the middle of the form, meaning the user must scroll to the top themselves. My form is in a react-modal. I played around with window.scrollTo but this of course won't work as I don't want to change the window location, rather the form in the modal.
I can't find many similar situations as mine as I have a form inside a modal.
This problem only applies to the mobile view and tablet view of my site. The desktop view is fine.
Would anyone have any hints as to what I should look at?
modal component
import React, { Component } from "react";
import Modal from "react-modal";
//Components
import QuoteForm from "./form.js";
class GetQuote extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
componentDidMount() {
Modal.setAppElement("body");
}
toggleModal = () => {
this.setState(prevState => {
return { showModal: !prevState.showModal };
});
};
render() {
return (
<div className="modalBtn-container">
<button className={this.props.type} onClick={this.toggleModal}>
{this.props.text}
</button>
<Modal
className={this.props.modalStyle}
isOpen={this.state.showModal}
onRequestClose={this.toggleModal}
contentLabel="popup modal form"
>
<QuoteForm toggleModal={this.toggleModal} />
</Modal>
</div>
);
}
}
export default GetQuote;
form component
import React, { Component } from "react";
//Components
import QuoteFormFirstPage from "./formFirstPage.js";
import QuoteFormSecondPage from "./formSecondPage.js";
class QuoteForm extends Component {
constructor(props) {
super(props);
this.state = {
page: 1
};
this.myRef = React.createRef();
this.nextPage = this.nextPage.bind(this);
this.previousPage = this.previousPage.bind(this);
}
nextPage() {
this.setState({ page: this.state.page + 1 });
this.scrollToMyRef();
}
previousPage() {
this.setState({ page: this.state.page - 1 });
}
scrollToMyRef = () => {
console.log(this.myRef.current.offsetTop);
window.scrollTo(0, this.myRef.current.offsetTop);
};
render() {
const { page } = this.state;
return (
<div className="quote-container" ref={this.myRef}>
{page === 1 && (
<QuoteFormFirstPage
close={this.props.toggleModal}
onSubmit={this.nextPage}
currentPage={page}
initialValues={this.props.initialValues}
enableReinitialize={true}
/>
)}
{page === 2 && (
<QuoteFormSecondPage
close={this.props.toggleModal}
previousPage={this.previousPage}
onSubmit={this.nextPage}
currentPage={page}
/>
)}
</div>
);
}
}
export default QuoteForm;
form first page
import React from "react";
import { Field, reduxForm } from "redux-form";
const QuoteFormFirstPage = props => {
return (
<div className="quote-form">
<form onSubmit={props.handleSubmit}>
<div className="cancel-button-container">
<button className="cancel-btn" onClick={props.close}>
x
</button>
</div>
<div className="containerForm-row">
<div className="containerForm-col">
<div className="field-container">
<Field
name={`your_details.title`}
component={selectField}
labelClass=" hidden required"
className="small"
>
<option value="Mr">Mr</option>
<option value="Ms">Ms</option>
<option value="Mrs">Mrs</option>
<option value="Dr">Dr</option>
</Field>
<Field
name="your_details.first_name"
type="text"
placeholder="Forename"
containerClass="field-group medium"
component={dgInput}
label="What is your forename?"
labelClass="title-left required"
validate={[required]}
/>
</div>
</div>
</div>
<div className="form-container-button-first">
<button type="close" onClick={props.close} className="form-back">
Cancel
</button>
<button type="submit" className="btn-quote">
Next
</button>
</div>
</form>
</div>
);
};
export default reduxForm({
form: "quote", // <------ same form name
destroyOnUnmount: false, // <------ preserve form data
forceUnregisterOnUnmount: true // <------ unregister fields on unmount
})(QuoteFormFirstPage);
form second page
import React from "react";
import { Field, reduxForm } from "redux-form";
const QuoteFormSecondPage = props => {
const { close, currentPage, handleSubmit, previousPage } = props;
return (
<div className="quote-form">
<form onSubmit={handleSubmit}>
<div className="cancel-button-container">
<button className="cancel-btn" onClick={close}>
x
</button>
</div>
<div className="form-row">
<Field
name="your_insurance_details.policy_start_date"
type="date"
placeholder={getCurrentDate()}
containerClass="field-group large"
component={dgInput}
label="When would you like your policy to start?"
labelClass="required"
validate={[required]}
/>
</div>
<div className="form-container-button">
<button type="button" className="form-back" onClick={previousPage}>
Back to property details
</button>
<button type="submit" className="btn-quote">
Next
</button>
</div>
</form>
</div>
);
};
export default reduxForm({
form: "quote", //Form name is same
destroyOnUnmount: false, // <------ preserve form data
forceUnregisterOnUnmount: true // <------ unregister fields on unmount
})(QuoteFormSecondPage);
I trimmed a lot of the form to try and make it more readable. As you can see in the form component, I tried using createRef and scrolling to it on lick of next page. But that uses window.scrollTo which won't work as I need to scroll to the top of the modal I'm in not the window!
Could you provide a stackblitz or anything else so we could implement something?
I would suggest to use an anchor and scroll to the element as described in How to scroll HTML page to given anchor?

to display a different component with each click (using hooks)

I want to display a different component with each button click.
I'm sure the syntax is wrong, can anyone help me? The browser doesn't load
I would love an explanation of where I went wrong
One component (instead of HomePage) should display on the App component after clicking the button. Help me to understand the right method.
Thanks!
App.js
import React, {useState} from 'react';
import './App.css';
import Addroom from './components/Addroom.js'
import HomePage from './components/HomePage.js'
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button onClick={()=>{setFlage({flag:true})}}>Addroom</button>
<button onClick={()=>{setFlage({flag:false})}}>HomePage</button>
{setState({flag}) && (
<div><Addroom index={i}/></div>
)}
{!setState({flag}) && (
<div><HomePage index={i}/></div>
)}
</div>
)
}
export default App;
HomePage
import React from 'react'
export default function HomePage() {
return (
<div>
HomePage
</div>
)
}
Addroom
import React from 'react'
export default function Addroom() {
return (
<div>
Addroom
</div>
)
}
I didn't test it but as i can see it should be something like this:
<button onClick={()=>setFlage(true)}>Addroom</button>
<button onClick={()=>setFlage(false)}>HomePage</button>
{flag && (
<div><Addroom index={i}/></div>
)}
{!flag && (
<div><HomePage index={i}/></div>
)}
You need to call setFlage function with argument of Boolean saying true or false and it changes the flag variable that you want to read.
Try the following.
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button
onClick={() => {
setFlage(true);
}}
>
Addroom
</button>
<button
onClick={() => {
setFlage(false );
}}
>
HomePage
</button>
{flag ? <Addroom /> : <HomePage /> }
</div>
);
}
You are missing render methods and also you should use setState for reactive rendering.( when you use state variables and once value changed render method will rebuild output so this will load your conditinal component.
https://jsfiddle.net/khajaamin/f8hL3ugx/21/
--- HTML
class Home extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Home</div>;
}
}
class Contact extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Contact</div>;
}
}
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
flag: false,
};
}
handleClick() {
this.setState((state) => ({
flag: !state.flag,
}));
console.log("hi", this.state.flag);
}
getSelectedComp() {
if (this.state.flag) {
return <Home></Home>;
}
return <Contact></Contact>;
}
render() {
console.log("refreshed");
return (
<div>
<h1>
Click On button to see Home component loading and reclick to load back
Contact component
</h1
<button onClick={() => this.handleClick()}>Switch Component</button>
{this.getSelectedComp()}
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"));

How to display the name of the clicked button in React?

They are working on the game of paper, stone, scissors. I would like to display its id in Result component after pressing one of the buttons. How can I do this?
App.js
import React, { Component } from "react";
import "./App.scss";
import SubmitInfo from "./SubmitInfo";
import ResultInfo from "./ResultInfo";
class App extends Component {
constructor(props) {
super(props);
this.test = this.test.bind(this);
}
test = id => {
//return <Result id={this.props.id}></Result>
console.log("test");
};
render() {
return (
<div>
<div className="board">
<div className="title_row">
<h1 className="title">Kamień, Papier, Nożyce</h1>
</div>
</div>
<div className="board">
<div className="submit_row">
<SubmitInfo id="papier" click={this.test} />
<SubmitInfo id="kamien" click={this.test} />
<SubmitInfo id="nozyce" click={this.test} />
<ResultInfo id={this.test} />
</div>
</div>
</div>
);
}
}
export default App;
SubmitInfo.js
Transfer to id props and click event, Then I render three buttons with different icons.
import React from "react";
import styles from "./submit.scss";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faHandPaper,
faHandScissors,
faHandRock
} from "#fortawesome/free-solid-svg-icons";
import ResultInfo from "./ResultInfo";
function Submit({ id, click }) {
if (id === "nozyce") {
return (
<button className="submit" onClick={click}>
<FontAwesomeIcon className="icon" icon={faHandScissors} />
</button>
);
} else if (id === "papier") {
return (
<button className="submit" onClick={click}>
<FontAwesomeIcon className="icon" icon={faHandPaper} />
</button>
);
} else if (id === "kamien") {
return (
<button className="submit" onClick={click}>
<FontAwesomeIcon className="icon" icon={faHandRock} />
</button>
);
}
}
export default SubmitInfo;
ResultInfo.js
Here I would like to display the id of the button clicked.
import React, { Component } from "react";
class ResultInfo extends Component {
render(props) {
return (
<div>
{" "}
{this.props.id}
{console.log(this.props.id)}
</div>
//<div></div>
);
}
}
export default ResultInfo;
You should use state. Initialize it in the constructor
constructor(props) {
super(props);
this.state = {
id: null,
};
}
Your test function, should set the state with the id
test = id => {
this.setState({
id,
});
};
ResultInfo component should recieve the state id as an attribute
<ResultInfo id={this.state.id} />
And, the buttons on SubmitInfo component should call the click function, with the id as a parameter
<button className='submit' onClick={() => click(id)}>Rock</button>
David's explanation is correct.
I was working on a codesandbox example on parallel, figured it'd help.
You have some additional errors, like exporting default SubmitInfo instead of Submit, and you can use conditional rendering of ResultInfo component.

Converting stateless to a class component react js

I have a component here that fills up a data for movies:
const MovieList = (props) => {
const movieItems = props.movies.map((movie) => {
return <MovieListItem key={movie._id} movie={movie} />
});
return(
<div className="container">
<h2 className="latest">MOVIE RESULTS</h2>
<div className="row">
{ movieItems }
</div>
</div>
);
};
Basically, I was planning to convert my next component where I am filling up my movieItems to a class component. This class has some helpers function along with a new function onClickImgMoreBtn whenever it was click it must console log something. But it doesnt work any more when I tried to convert it class component. Even my helper classes wont work either.
Here's my original stateless function:
import React from 'react';
const helper = require('../../helpers/helper_movies')
const MovieListItem = ({movie}) => {
return (
<div>
{helper.mappingComma(movie.genre)}
{helper.getExcerpt(movie.overview)}
<button className="btn btn-secondary float-right btn-sm" href={movie._id} onClick={this.onClickImgMoreBtn}>MORE</button>
</div>
);
};
export default MovieListItem;
And here's how i change it along with the new function:
import React, { Component } from 'react';
const helper = require('../../helpers/helper_movies');
class MovieListItem extends Component{
constructor(props){
super(props);
}
onClickImgMoreBtn(){
console.log('hi there!');
}
render(){
return (
<div>
{helper.mappingComma(this.props.genre)}
{helper.getExcerpt(this.props.overview)}
<button className="btn btn-secondary float-right btn-sm" href={this.props._id} onClick={this.onClickImgMoreBtn}>MORE</button>
</div>
);
}
}
export default MovieListItem;
Any idea what am i missing? did i properly converted it?
In your stateless component you destructure movie from the props and use movie.genre, movie._id and movie.overview and hence in the component you need to use
this.props.movie.genre, this.props.movie._id and this.props.movie.overview
render(){
return (
<div>
{helper.mappingComma(this.props.movie.genre)}
{helper.getExcerpt(this.props.movie.overview)}
<button className="btn btn-secondary float-right btn-sm" href={this.props.movie._id} onClick={this.onClickImgMoreBtn}>MORE</button>
</div>
);
}
or better, destructure movie form props in render
render(){
const { movie } = this.props;
return (
<div>
{helper.mappingComma(movie.genre)}
{helper.getExcerpt(movie.overview)}
<button className="btn btn-secondary float-right btn-sm" href={movie._id} onClick={this.onClickImgMoreBtn}>MORE</button>
</div>
);
}
Also since you are not doing anything in the constructor, you need to have the constructor defined.

Reactjs controlling state in parent from grand child

I have at my top level:
import React from 'react';
import JobList from './JobList';
import RightPanel from './RightPanel';
import JobStore from '../../stores/JobStore';
import LoadJobsScreen from '../../actions/jobs-screen/LoadJobsScreen';
import Modal from '../global/Modal';
export default class JobScreen extends React.Component {
static contextTypes = {
executeAction: React.PropTypes.func.isRequired
};
componentWillMount() {
this.toggleModal = this.toggleModal.bind(this);
this.state = {open: false}
this.context.executeAction(LoadJobsScreen, this);
}
toggleModal() {
this.setState({
open: !this.state.open
});
console.log(this.state.open);
}
render() {
return (
<div className="jobs-screen">
<div className="col-xs-12 col-sm-10 job-list"><JobList /></div>
<div className="col-xs-12 col-sm-2 panel-container">
<div className="right-panel pull-right"><RightPanel /></div>
</div>
<Modal open={this.state.open} toggleModal={this.toggleModal} />
</div>
);
}
}
Modal is:
import React from 'react';
class Modal extends React.Component {
constructor() {
super();
}
render() {
let open = this.props.open;
return (
<div className={'modal fade'+(open ? '' : ' hide')} tabindex="-1" role="dialog">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">{this.props.title}</h4>
</div>
<div className="modal-body">
{this.props.children}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" className="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
)
}
}
export default Modal;
But I want to open and close it (as well as send data to it later) from within a deeper component:
import React from 'react';
import UrgencyToggle from './UrgencyToggle';
import ApproveButton from './ApproveButton';
import ShippingTable from './ShippingTable';
import DropdownButtonList from '../global/DropdownButtonList';
export default class Job extends React.Component {
constructor(props) {
super(props);
}
setUrgency(urgency) {
actionContext.dispatch('SET_JOB_URGENCY', {
data: urgency
})
};
render() {
return ( <
span className = "name" > < img src = "/images/system-icons/pencil.png"
onClick = {
this.toggleModal
}
width = "13" / > < /span>
)
}
};
Obviously this doesn't work because toggleModal is all the way up in JobScreen. How can I execute the function in the grandparent from this depth?
If your JobScreen, JobList, Joband Modal components are designed to be tightly coupled, i.e not meant to be separated from each other in future, you could use the JobScreen as a High Order Component to store the state of your modal and passing down the tree as prop a callback function to update this state (I simplified a bit and made some assumptions on missing components) :
export default class JobScreen extends React.Component {
constructor(props) {
super(props);
this.displayName = 'JobScreen'
this.state = {
modalOpened: false,
modalTitle: "",
}
}
componentWillMount() {
this.context.executeAction(LoadJobsScreen, this);
}
toggleModal() {
this.setState({
modalOpened: !this.state.modalOpened
});
}
editModalTitle(title) {
this.setState({
modalTitle: title
})
}
render() {
return (
<div className="jobs-screen">
<div className="col-xs-12 col-sm-10 job-list">
<JobList
toggleModal={() => this.toggleModal() /* auto binding with arrow func */}
editModalTitle={(title) => this.editModalTitle(title)} />
</div>
<Modal
open={this.state.modalOpened}
title={this.state.modalTitle}/>
</div>
);
}
}
const JobList = (props) => {
const jobs = [1,2,3]
return (
<ul>
{jobs.map(key => (
<li key={key}>
<Job
toggleModal={props.toggleModal}
editModalTitle={props.editModalTitle}/>
</li>
))}
</ul>
);
}
const Job = (props) => {
return (
<span className="name">
<img
src="/images/system-icons/pencil.png"
width="13"
onClick={(e) => {
props.toggleModal(e)
props.editModalTitle("new title") //not very efficient here cause we're updating state twice instead of once, but it's just for the sake of the example
}}/>
</span>
);
}
I deliberately not mentions how to modify the modal children this way cause it's an absolute anti-pattern. So, you should definitively look at something like Redux which provides a way to manage the state of your application and dispatch action from wherever you want to update in a 'one way data binding' way. I've the impression you're trying to bypass the React internal mechanisms by using context as an action dispatcher. So, Redux (or another Flux library) will be your best bet here.

Categories