ReactJS Component not displaying - javascript

I'am very new in the programming world and React (using the COVID-19 time to get better...). I'm trying to render a component when the user is clicking a register button. My goal is to display it as a pop-up in the middle of the screen for the user to fill a form. (I'm using Visual studio code and react app generator)
I can't make it happen, if I console.log the result true/false ( depending on a condition) it works correctly so I guess the problem is the way I " call" the component.
If anyone could point toward the good direction I would glady appreciate !
The App class where the handler function is calling the supposed popup div
import React from "react"
import Header from "./UI/Header";import RegisterWindow from "./UI/RegisterWindow"; import Footer from "./UI/Footer"; import MainSection from "./UI/MainSection";
import "./index.css"
class App extends React.Component{
constructor(){
super()
this.state ={
registerIsShowed: false
}
this.handleRegister = this.handleRegister.bind(this)
}
handleRegister(){
this.setState({
registerIsShowed: !this.state.registerIsShowed
})
const isShowed = this.state.registerIsShowed;
return isShowed ? <RegisterWindow /> : null
}
render(){
return (
<div>
<Header register={this.handleRegister} />
<MainSection />
</div>
)}
}
export default App
This is the Header code where the button that triggers the opening is located
import React from "react"
function Header(props) {
return (
<header>
<nav className="navbar-header">
<p className="header-data"></p>
<ul className="navbar-menu-header">
<li><button onClick={props.register}>Registrar</button></li>
<li><button>Entrar</button></li>
</ul>
</nav>
</header>
)
}
export default Header
and finally the Component that is supposed to show up
import React from "react"
class RegisterWindow extends React.Component{
render(){
return (
<div className="register-window">
<div>
<form>
<input name="firstName" placeholder="First name" type="text" />First Name
<input name="lasttName" placeholder="Last name" type="text" />Last Name
</form>
</div>
</div>
)
}
}
export default RegisterWindow
thank you,

The RegisterWindow component must be included in the Render lifecycle function in a class component, or within a return statement of a functional component. Your App component is class based so it must contain a render() method.
Setting the state is asynchronous, so even if you could render the component from the handleRegister() callback in a class component, the state update wouldn't be immediate so your synchronous logic to display the RegisterWindow component would fail.
Try something like this:
handleRegister() {
this.setState({
registerIsShowed: !this.state.registerIsShowed
});
}
render() {
return (
<>
{this.state.registerIsShowed && <RegisterWindow />}
<div>
<Header register={this.handleRegister} />
<MainSection />
</div>
</>
)
}
this.state.registerIsShowed && <RegisterWindow /> is an example of Conditional Rendering.
To make the RegisterWindow appear floating above the MainSection, you can style it with an absolute position.

Related

Alter React parent component state

I am trying to create a simple SPA (without Router). It has also a simple structure: a component per section:
Home
Services
Products
Product
Modal
Contact us
As you can see the component Products has two sub-components Product and Modal. These are iterated so many times as JSON objects there are:
Products.js
import React, { Component } from "react";
import ReactHtmlParser from "react-html-parser";
import "./Products.css";
import { products } from "./products.json";
import Product from "./Product/Product";
import Modal from "./Modal/Modal";
class Products extends Component {
render() {
return (
<section id='products'>
<div className='container'>
<div className='row'>
{products.map(product => {
return (
<div>
<Product
image={"/img/" + product.image}
name={product.name}
target={product.target}
/>
<Modal
id={product.target}
title={product.name}
body={ReactHtmlParser(product.body)}
/>
</div>
);
})}
</div>
</div>
</section>
);
}
}
export default Products;
Each product has a More Info button what opens the modal and this has another button Budget ("Presupuestar"):
That function should "change the state" of Contact us component (a simple contact us form):
The component has the following code:
Contact.js
import React, { Component } from "react";
import "./Contact.css";
class Contact extends Component {
constructor() {
super();
this.state = { budget: "Contact" };
}
render() {
return (
<section id='contact'>
<div className='container'>
<div className='row'>
<div className='col-xs-12 col-md-6'>
<div className='contact-form'>
<form>
...
{/* Subject */}
<div className='form-group'>
<div className='input-group'>
<span className='input-group-addon' />
<input
type='text'
className='form-control'
id='subject'
aria-describedby='Subject'
placeholder='Subject'
readonly='readonly'
value={this.state.budget}
/>
</div>
{/* /form-group */}
</div>
{/* /Subject */}
...
</form>
</div>
</div>
</div>
</div>
</section>
);
}
}
I guess then I should create a function in the Modal component to trigger with an onClick="setSubject" in the Budget ("Presupuestar") button. What I don't know is how to alter the other component's state.
A quick summary: I have to make the following state update:
I was reading this similar question but I didn't get how to apply in my scenario. Any ideas?
I think you should either but the clickHandler function of the button in the App component that wrap the whole components and then pass it to the Products component then to Modal component but it's not a good practice,
Or you can use Redux a state management system that let you control your state through the whole app.
First of all, you don't need a function to change the state of another component. The smart way to do that is using an intermediary thing to connect 2 component together. There is two way to solve this problem.
The easiest way is you can transfer subject via URL (URL is "the intermediary thing"). When you click the button Presupuestar you can change URL to page contact like this:
/contact?subject=whatever you want
Then, at Contact component, you just need to parse URL to get subject (you can see this question to know how to parse from URL). You can see my example.
The second way is creating a service use singleton pattern to transfer subject from Modal to Contact form. You can see my example.
You can achieve this like this
Create a main app component which will contain all these these three comps
Add a function in app component "changeContacts"
Send it to both the product as well as contacts
Here is an explanation
class App extends Component {
render() {
return (
<div>
<Contact ref="contacts"/>
<Products changeContacts={this.changeContacts} />
</div>
);
}
changeContacts = (newState) => {
this.refs.contacts.changeState(newState)
};
}
class Contact extends Component {
state = { text:"Old Text" }
render() {
return ( <div style={{fontSize:50,backgroundColor:'red'}}>{this.state.text}</div> );
}
changeState = (newState) =>{
this.setState(newState);
}
}
class Modal extends Component {
render() {
return ( <div onClick={() => this.props.onClick({text:"New State Text"})}>This is a modal</div> );
}
}
class Products extends Component {
state = { }
render() {
return ( <div>
<h1>Products List</h1>
<Modal onClick={this.props.changeContacts} />
<Modal onClick={this.props.changeContacts}/>
<Modal onClick={this.props.changeContacts}/>
</div> );
}
}

React Router redirect after submitting a form using the input value

Form.js
What I wish to get out of this form is a link like '/search/inputValue/' so from another component I can extract the parameter. What I get instead is just '/search/' without the input value.
import React from 'react';
import { Link } from 'react-router-dom';
class Form extends React.Component {
state = {
searched: ''
}
onSubmit = (e) => {
const keyword = e.target.elements.keyword.value;
this.setState({ searched: keyword });
}
render(){
return (
<form className="form-inline" onSubmit={this.onSubmit}>
<div className="form-group">
<input type="text" className="form-control" name="keyword" placeholder="Image keyword" />
<Link to={ `/search/${this.state.searched}`}>
<button className="btn btn-primary">Search</button>
</Link>
</div>
</form>
);
}
};
export default Form;
I have noticed that the state updates its value after a second submit with the older input value, so the problem might be from here.
This can be checked by removing the Link tag, preventDefault and console log the input value. The first one is blank and the second one is with the previous input value.
My whole app is sorted, I just need to figure how to submit to a link from an input.
Router.js
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import App from '../App';
import SearchPage from './SearchPage';
const Router = () => (
<BrowserRouter>
<Switch>
<Route path="/" component={App} exact />
<Route path="/search/:keyword" component={SearchPage} />
</Switch>
</BrowserRouter>
);
export default Router;
Basically after finally getting to a computer to help you, I realized one of my first responses was correct.
You needed to:
Bind the handleChange method. All methods you define in an object passed to React.createClass will be automatically bound to the component instance.
Every state mutation will have an associated handler function. This makes it straightforward to modify or validate user input. That is why we have the handleChange function.
Since the value attribute is set on our form element, the displayed value will always be this.state.value, making the React state the source of truth. Since handleChange runs on every keystroke to update the React state, the displayed value will update as the user types..
Since he is not submitting a form actually, this is the correct way to do this. However, if you were submitting form, ditch the dynamic link and use the form action property.
import React from 'react';
import { Link } from 'react-router-dom';
class App extends React.Component {
/** Left some things in here commented out,
incase you start doing form submissions. Instead of a dynamic link.
**/
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
/** If you start submitting forms
this.handleSubmit = this.handleSubmit.bind(this);
**/
}
handleChange(event) {
this.setState({value: event.target.value});
}
/** If you start submitting forms, add onSubmit={this.onSubmit} to form action
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
**/
render() {
return (
<div>
<form className="form-inline">
<div className="form-group">
<input type="text" value={this.state.value} onChange={this.handleChange} className="form-control" name="keyword" placeholder="Image keyword" />
<Link to={`/search/${this.state.value}`}>
<button className="btn btn-primary">Search</button>
</Link>
</div>
</form>
</div>
);
}
}
export default App;
I think you should not wrap the submit button with a Link.
But you should add a e.preventDefault() in your onSubmit() to prevent form to be submitted and prevent the browser to redirection/refresh.
You should add the redirection directly at the end of your onSubmit method with the history API (https://reacttraining.com/react-router/web/api/history)
I've struggled with the same issue.
I am on a project with React Router 4.
What I found so far is,
It depends on how you set up your Routes.
If you use component attribute to render a component, such as <Route to="/path" component={Name} />, the component will have data (history, location and match)
If so, you can use input value to redirect using history.path etc. See the code below.
However, if you use render method such as <Route to="/path" render={()=> <Component />} /> to pass data to a child component, the rendered component have nothing.
class Home extends Component {
handleSubmit = e => {
e.preventDefault();
let teacherName = this.name.value;
let teacherTopic = this.topic.value;
let path = `teachers/${teacherTopic}/${teacherName}`;
// this is the part !!!
this.props.history.push(path);
};
render() {
return (
<div className="main-content home">
<h2>Front End Course Directory</h2>
<p>
This fun directory is a project for the <em>React Router Basics</em> course on Treehouse.
</p>
<p>
Learn front end web development and much more! This simple directory app offers a preview
of our course library. Choose from many hours of content, from HTML to CSS to JavaScript.
Learn to code and get the skills you need to launch a new career in front end web
development.
</p>
<p>
We have thousands of videos created by expert teachers on web design and front end
development. Our library is continually refreshed with the latest on web technology so you
will never fall behind.
</p>
<hr />
<h3>Featured Teachers</h3>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Name" ref={input => (this.name = input)} />
<input type="text" placeholder="Topic" ref={input => (this.topic = input)} />
<button type="submit">Go!</button>
</form>
</div>
);
}
}
It was mentioned nowhere while I am learning, was really pain in the ass to figure out just this small fact. Anyway, happy coding!

Adding componentDidMount to React component results in syntax error

my home component has:
const Home = () => (
<div>
<Head title="Home" />
<Nav />
<div className="container o_block u_blue">
<div className="notification">
This container is <strong>centered</strong> on desktop.
</div>
</div>
</div>
)
export default Home
and I'm trying to add some DOM manipulation into that component:
componentDidMount() {
let burger = document.querySelector('.burger');
let nav = document.querySelector('#'+burger.dataset.target);
burger.addEventListener('click', function(){
burger.classList.toggle('is-active');
nav.classList.toggle('is-active');
});
}
const Home = () => (
<div>
<Head title="Home" />
<Nav />
<div className="container o_block u_blue">
<div className="notification">
This container is <strong>centered</strong> on desktop.
</div>
</div>
</div>
)
export default Home
but unfortunately I am getting a:
SyntaxError: Unexpected token, expected ";" (8:20)
what am I doing wrong and where should I put the method?
Home is a presentational component in your code. Which means a presentational component is like a pure function in Java Script. A Presentational component doesn’t call any React life cycle method and doesn’t modify React state. It only takes props and returns jsx elements. This is also called as stateless component in react.
If you want to play with React life cycle methods then you should go with statefull component.
componentDidMount is one of React life cycle method so it’s not accessible in presentational or functional components in React.
Edit:
If you want to do DOM manipulation before component initial render then do DOM manipulation in componentWillMount() method but please see this method is deprecated in latest React versions.
If you want to do DOM manipulation after first render then do that in componentDidMount() method. This is the one wr you also make axios calls and do setState accordingly. I would recommend you to go with componentDidMount().
import React, { Component} from "react";
export default class Home extends Component {
componentDidMount() {
let burger = document.querySelector('.burger');
let nav = document.querySelector('#'+burger.dataset.target);
burger.addEventListener('click', function(){
burger.classList.toggle('is-active');
nav.classList.toggle('is-active');
});
}
render(){
return(
<div>
<Head title="Home" />
<Nav />
<div className="container o_block u_blue">
<div className="notification">
This container is <strong>centered</strong> on desktop.
</div>
</div>
</div>
)
}
}
Please excuse me if there are any typo error because I am answering in my mobile
You need to transform your component into a class-based component, like this :
export default class Home extends React.Component {
render() {
// Return the JSX code
}
componentDidMount() {
// Your init code
}
}
But I really suggest you to take a look at the official React doc as it's a fairly simple mistake.

this.setState React bind issue

I am attempting to pass the changePage class method into a child component called SideBar. When the changePage method is then triggered by an onClick event in the child component I receive the following error:
Uncaught TypeError: this.SetState is not a function
From what I could find in other similar posts I need to bind the changePage method to this. I have done that but I still can't manage to get is to work.
I also saw many suggestions to use ES6 arrow functions for my methods but I get the exact same error message if I do.
I'm still quite new at web development and any help would be appreciated.
Parent Component called Main:
import React from 'react';
import Content from './Content';
import Sidebar from './Sidebar';
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedPage: 'home',
pages: ['home','about','skills','contact'],
};
this.changePage = this.changePage.bind(this);
}
changePage(page) {
console.log(page);
this.SetState({
selectedPage: page,
pages: ['home','about','skills','contact']
});
};
render() {
return (
<div>
<div id="sidebar" className="side-bar">
<Sidebar
changePage={this.changePage}
selectedPage={this.state.selectedPage}
pages={this.state.pages}
/>
</div>
<div id="main" className="main-content">
<Content
selectedPage={this.state.selectedPage}
/>
</div>
</div>
)
}
}
Child Component:
import React from 'react';
export default class Sidebar extends React.Component {
render() {
console.log("content props",this.props);
const buttons = this.props.pages.map(button =>
<span
className='nav-button'
id={button}
key={button}
onClick={() => this.props.changePage(button)}
>
<img src={`./app/images/${button}.svg`} />
</span>
);
return (
<div>
<span>
<img className='headshot' src='./app/images/headshot.jpg' />
</span>
<div className='nav-container'>
{buttons}
</div>
</div>
)
}
}
You have syntax error: this.SetState. Change it to this.setState.
The typo in setState was causing the error, but you may want consider a cleaner way to bind the parent this on the changePage prop. Your way works, but if you changed sidebar Component inclusion to:
<Sidebar
changePage={ (page) => this.changePage(page)}
selectedPage={this.state.selectedPage}
pages={this.state.pages}
/>
That may make it clearer what is going on (changePage prop is a function which takes one parameter and passes that parameter to instance method this.changePage), and removes the binding gymnastics in the constructor.

How to input data in one React component and have it render in another?

I'm learning React using JSX and ES6 and I've got a pretty decent handle on how to create components and route to different views using ReactRouter4.
What I still haven't been able to figure out is for example how i can create an Admin page where I input the details of a work for my portfolio and have all the works render on the another page, presumably Portfolio page.
Here's what I've got.
App.js loads the Portfolio.js component
import React from 'react';
import Navigation from './Navigation';
import Title from './Title';
import Portfolio from './Portfolio';
class App extends React.Component {
render() {
return(
<div className="container-fluid">
<div className="container">
<div className="row">
<div className="col-sm-12">
<Navigation />
<Title title="kuality.io"/>
<section className="app">
<Portfolio works={this.props.works} />
</section>
</div>
</div>
</div>
</div>
)
}
}
export default App;
The Portfolio.js component has a constructor to bind a unique method named addWork(), the React methods componentWillMount() and componentWillUnmount() to handle state, and the default render(). One more thing to mention about this component is that it's calling a component called ../base which has all the details to an online DB via Firebase. So if that's relevant as to where it is place, then take that into consideration otherwise don't sweat it.
import React from 'react';
import Work from './Work';
import Admin from './Admin';
import base from '../base';
class Portfolio extends React.Component {
constructor() {
super();
this.addWork = this.addWork.bind(this);
// getInitialState
this.state = {
works: {}
};
}
componentWillMount() {
this.ref = base.syncState(`/works`
, {
context: this,
state: 'works'
})
}
componentWillUnmount() {
base.removeBinding(this.ref);
}
addWork(work) {
// update our state
const works = {...this.state.works};
// add in our new works with a timestamp in seconds since Jan 1st 1970
const timestamp = Date.now();
works[`work-${timestamp}`] = work;
// set state
this.setState({ works });
}
render() {
return(
<div>
<section className="portfolio">
<h3>Portfolio</h3>
<ul className="list-of-work">
{
Object
.keys(this.state.works)
.map(key => <Work key={key} details={this.state.works[key]}/>)
}
</ul>
</section>
</div>
)
}
}
export default Portfolio;
Inside of the Object i'm mapping through the Work component that is just a list item I have made another component for and isn't really relevant in the question.
Finally I have the Admin.js and AddWorkForm.js components. I abstracted the AddWorkForm.js so that I could use it elsewhere if need be, basically the main idea behind React Components, so that's why I chose to do it that way.
import React from 'react';
import Title from './Title';
import AddWorkForm from './AddWorkForm';
class Admin extends React.Component {
constructor() {
super();
this.addWork = this.addWork.bind(this);
// getInitialState
this.state = {
works: {}
};
}
addWork(work) {
// update our state
const works = {...this.state.works};
// add in our new works with a timestamp in seconds since Jan 1st 1970
const timestamp = Date.now();
works[`work-${timestamp}`] = work;
// set state
this.setState({ works });
}
render() {
return(
<div className="container-fluid">
<div className="container">
<div className="row">
<div className="col-sm-12">
<Title title="Admin"/>
<section className="admin">
<AddWorkForm addWork={this.addWork} />
</section>
</div>
</div>
</div>
</div>
)
}
}
export default Admin;
and the AddWorkForm.js component which is basically a form that onSubmit creates and object and resets the form
import React from 'react';
class AddWorkForm extends React.Component {
createWork(event) {
event.preventDefault();
console.log('Creating some work');
const work = {
name: this.name.value,
desc: this.desc.value,
image: this.image.value
}
this.props.addWork(work);
this.workForm.reset();
}
render() {
return(
<form ref={(input) => this.workForm = input} className="work-edit form-group" onSubmit={(e) => this.createWork(e)}>
<input ref={(input) => this.name = input} type="text" className="form-control" placeholder="Work Title"/>
<textarea ref={(input) => this.desc = input} type="text" className="form-control" placeholder="Work Description"></textarea>
<input ref={(input) => this.image = input} type="text" className="form-control" placeholder="Work Image"/>
<button type="submit" className="btn btn-primary">+Add Work</button>
</form>
)
}
}
export default AddWorkForm;
Here is the file that includes where I'm using ReactRouter:
import React from 'react';
import { render } from 'react-dom';
// To render one method from a package user curly brackets, you would have to know what method you wan though
import { BrowserRouter, Match, Miss} from 'react-router';
import './css/normalize.css';
import './css/bootstrap.css';
import './css/style.css';
// import '../js/bootstrap.js';
import App from './components/App';
import WorkItem from './components/WorkItem';
import Capability from './components/Capability';
import Connect from './components/Connect';
import NotFound from './components/NotFound';
import Admin from './components/Admin';
const Root = ()=> {
return(
<BrowserRouter>
<div>
<Match exactly pattern="/" component={App} />
<Match pattern="/work/:workId" component={WorkItem} />
<Match exactly pattern="/capability" component={Capability} />
<Match exactly pattern="/connect" component={Connect} />
<Match exactly pattern="/admin" component={Admin} />
<Miss component={NotFound} />
</div>
</BrowserRouter>
)
}
render (<Root />, document.querySelector('#main'));
So here's what I've tried and failed to accomplish, and it's likely some kind of this.props solution that I haven't been able to define, I need to create the work in Admin.js component, which creates the object and then have it throw that object to Portfolio.js component so it can render it via the Work.js component and it doesn't add the object to the DB.
This works when i put all the components on the same page, which isn't ideal because then anyone accessing my Portfolio could add a work. Sure I could start the process of learning authentication and how to make that component appear or disappear based on user credentials, but I'd much rather also learn the very valuable skill of being able to have my admin page on a separate view all together because I see another application for learning to do so.
Would love to hear others opinions on this and where they may be able to determine I'm failing here.
Btw, I realize I have other components like Nav.js and Title.js but they are not necessary in order to illustrate the example.
Thank you.
You can pass components as props and when using React Router you can have named components.
For data sharing between siblings is better advised to have the data on a parent component although you could use context, but this is not advised and may be unacessible on future versions.
If you need to create something on another component (don't know why) you could pass a function that would render it.

Categories