Trying to rewrite App.js as a class - reactjs

I am trying to re-write the entry point App.js javascript file as a class. I'm doing this because I want to call an onClick function when a user clicks on the log out link.
Here is the current code:
import React, { PropTypes } from 'react';
import { IndexLink, Link } from 'react-router';
const App = (props) => {
return (
<div>
<IndexLink className="mypad" to="home">Home</IndexLink>
<Link className="mypad" to="">Log Out</Link>
<br/>
<br/>
{props.children}
</div>
);
};
App.propTypes = {
children: PropTypes.element
};
export default App;
Here was my attempt at re-writing this as a class. It doesn't work. I get the error: 'ReferenceError: props is not defined'
class App extends React.Component {
constructor(props) {
super(props);
}
render(){
return (
<div>
<IndexLink className="mypad" to="home">Home</IndexLink>
<Link className="mypad" to="">Log Out</Link>
<br/>
<br/>
{props.children}
</div>
);
}
}
App.propTypes = {
children: PropTypes.element
};
export default App;

You need to do this.props once its a class
<div>
<IndexLink className="mypad" to="home">Home</IndexLink>
<Link className="mypad" to="">Log Out</Link>
<br/>
<br/>
{this.props.children}
</div>
when you write the component as a function, props gets passed in as an argument. When you do it as a class, props are bound to the instance of the component object.

add this => this.props.children

Related

Uncaught TypeError: this.props.history is undefined

learning on Udemy part of the course work isnt working for me despite using the same codes as the video. The code should allow me to do a search based on username with option to go directly to the profile or a database of results. But I am getting Uncaught TypeError: this.props.history is undefined at main.js
Main.js
import React,{Component} from 'react';
import { BrowserRouter as Router,Route ,Routes} from 'react-router-dom';
class Main extends Component{
constructor(props){
super(props);
this.state={
Data:'',
checked:false
};
}
handleChange=(e)=>{
this.setState(()=>({
checked:!this.state.checked
}))
}
Search= (e)=> {
console.log(this.state.Data)
if(this.state.Data==='')return alert('Search field cannot be empty!')
if(this.state.checked){
this.props.history.push({
Pathname:`/Specific/${this.state.Data}`,
})
}else{
this.props.history.push({
Pathname:`/Data/${this.state.Data}`,
})
}
}
render(){
return(
<React.Fragment>
<section className="mainPage">
<center>
<div className="main" id="main">
<div className="container">
<div className="row">
<div className="col-lg-12 col-md-12">
<div className="main__text-container">
<h1 className="main__title">
GitFetch - Profile Finder for GitHub
</h1>
<p className="main__subtitle">
Check out the repos, followers and more, just by entering a username!
</p>
</div>
<div className="container">
<div className="check">
<input className="" type="checkbox" name="checked" onChange={this.handleChange} value={this.state.checked}/>
Go Direct to The user Profile
</div>
<input type="text" id="search" name="Data" className="btn btn-outline-primary"
placeholder={this.state.checked?'Go to user profile':'Enter username'}
onChange={(e)=>{this.setState({[e.target.name]:e.target.value})}}
value={this.state.Data}
/>
<span>
<button onClick={this.Search} className="btn btn-outline-primary">Search</button>
</span>
</div>
</div>
</div>
</div>
</div>
</center>
</section>
</React.Fragment>
);
}
}
export default Main;
App.js
import React,{Component} from 'react';
import './App.css';
import Nav from './components/nav/nav';
import Main from './components/github/main';
import { BrowserRouter as Router,Route ,Routes} from 'react-router-dom';
class App extends Component{
constructor(props){
super(props);
this.state={};
}
render(){
return(
<React.Fragment>
<Nav/>
<Router>
<Routes>
<Route exact path='/' element={<Main/>}/>
</Routes>
</Router>
</React.Fragment>
);
}
}
export default App;
changed code to
import React,{Component} from 'react';
import withRouter from '../withRouter';
class Main extends Component{
constructor(props){
super(props);
this.state={
Data:'',
checked:false
};
this.Search=this.Search.bind(this);
}
handleChange=(e)=>{
this.setState(()=>({
checked:!this.state.checked
}))
}
Search= (e)=> {
console.log(this.state.Data)
if(this.state.Data==='')return alert('Search field cannot be empty!')
if(this.state.checked){
this.props.history.push({
Pathname:`/Specific/${this.state.Data}`,
})
}else{
this.props.navigate({
Pathname:`/Data/${this.state.Data}`,
})
}
}
but i am still unable to redirect users to the specified path with this.props.navigate({
Pathname:/Data/${this.state.Data},
})
your props are empty and don't have instance of history , to provide history instance you need to wrap your component with export default withRouter(Main)
but withRouter has been deprecated in react-router-dom v6 so if you are using this version or above then kindly write a HOC for withRouter
here is a reference to use withRouter for v6 and above version of react-router-dom
https://github.com/remix-run/react-router/issues/7256

Splitting code into files using context api

I'm using context API for the first time.I can understand how to write the code in a single page.But I can't understand how to split it into several files.When I split it as below it gives me an error saying that I have not exported MyProvider.But I have done it.I searched for many video tutorials but I couldn't find a one that matches to my problem
App.js
import {MyProvider} from './Context';
import {myContext} from './Context';
class Name extends Component {
render() {
return (
<div>
<myContext.Consumer>
{(context) => (
<React.Fragment>
<h1>Hello, {context.state.name}</h1>
<h3>{context.state.address}</h3>
</React.Fragment>
)}
</myContext.Consumer>
</div>
);
}
}
class Age extends Component {
render() {
return (
<div>
<myContext.Consumer>
{(context) => (
<React.Fragment>
<h1>You are {context.state.age} years old</h1>
<h3>You were born in - {context.state.dob}</h3>
</React.Fragment>
)}
</myContext.Consumer>
</div>
);
}
}
class App extends Component {
render() {
return (
<MyProvider>
<div className="App">
<Name />
<Age />
</div>
</MyProvider>
);
}
}
export default App;
Context.js
export const myContext = React.createContext();
class MyProvider extends Component {
state = {
name: "Sam",
address: "No.35,Main Street,Galle",
age: 50,
dob: "1970-10-21"
}
render() {
return (
<myContext.Provider value={{
state: this.state
}}>
{this.props.children}
</myContext.Provider>
)
}
}
export default MyProvider;
You have a mistake in import. When you export as default, you should import it without curly braces.
export default CLASS
import CLASS from
Or you can export your class as export class, then you will import it as you do:
export class A
import { A }
UPD:
Please take a look at
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export

Pass props between siblings (React)

I've got this functional component
import React from 'react'
function Header(){
return(
<div className='header'>
<h2>Logo</h2>
<p>Log In</p>
</div>
)
}
export default Header
When I will clicked on "Log In" I want to change my background-color in this class in class 'MainContainer'. Im a beginner in React. How Can I do it?
import React from 'react'
import RegistrationForm from '../LoginPage/RegistrationForm'
import './style2.scss'
class Main extends React.Component{
constructor(param){
super(param)
}
render(){
return(
<div className="mainContainer">
<RegistrationForm/>
</div>
)
}
}
export default Main
EDIT: Here is the class where I called this classes:
import React from 'react'
import Header from './components/LoginPage/Header'
import Main from './components/LoginPage/Main'
function App() {
return (
<div>
<Header/>
<Main/>
</div>
)
}
export default App;
Props can be passed from parent to child, not between siblings. So make a state in the App component and pass it down to Main and Header.
App
function App() {
const [loggingIn, setLoggingIn] = React.useState(false);
return (
<div>
<Header setLoggingIn={setLoggingIn} />
<Main loggingIn={loggingIn} />
</div>
)
}
Main
class Main extends React.Component{
constructor(param){
super(param)
}
render(){
return(
<div className="mainContainer" style={{ background: this.props.loggingIn ? 'red' : 'inherit' }}>
<RegistrationForm/>
</div>
)
}
}
Header
function Header({ setLoggingIn }){
return(
<div className='header'>
<h2>Logo</h2>
<p onClick={() => setLoggingIn(true)}>Log In</p>
</div>
)
}

I can declare a method Meteor onLogout within a constant?

I can declare a method within a constant, I try to implement the onLogout method in the navbar but I get an error in the method by saying 'js [;] expected' or require declaring the class in this way:
export default class Landing extends React.Component
import React from 'react';
import {Link} from 'react-router'
import { Accounts } from 'meteor/accounts-base';
const NavbarLanding = () => {
onLogout() {
Meteor.logout();
};
return (
<div className="navbar-landing">
<nav>
<div>
<ul className="ul-landing">
<img src="/images/fly_paper.svg"></img>
<li className="navbar-title"><a>Landing</a></li>
{/* <img border="0" height="40" hspace="0" src="/images/fly_paper.png" width="80" /> */}
{/* onClick={this.onLogout.bind(this) */}
{/* btn-primary */}
<div className="navbar-menu">
<li><a>acerca</a></li>
<li><a>portafolio</a></li>
<li><a>contacto</a></li>
<button className="btn"onClick={this.onLogout.bind(this)}>Logout</button>
</div>
</ul>
</div>
</nav>
</div>
);
};
export default NavbarLanding;
NavbarLanding.reactProptype = {
title: React.PropTypes.string.isRequired
};
You made an error declaring your component, it should be like this:
export default class NavbarLanding extends React.Component {
onLogout() {
Meteor.logout();
}
render() {
return (
// your html here
);
}
};
NavbarLanding.reactProptype = {
title: React.PropTypes.string.isRequired
};

Passing state in React from two different components

I have a TopNab bar (component), which contains a SearchBar component. My Main component is rendering out TopNav, the main container component (Profile), and the Footer component. I want my SearchBar component to pass its state to the main container component so that the Profile component can use it.
What I am trying to build:
A user types a name into search bar and submits.
The profile matching the user name is displayed in the main container component.
Right now I have a component that can render out user profiles. I also have a component thats state updates to the user submitted value. What I need to do is pass this user submitted value to my profile component in order to render out the correct profile.
Is this possible or do I need to rebuild my components so the search is included in the Profile component?
SearchBar
import React, { Component, PropTypes } from 'react';
import Profile from './Profile';
class SearchBar extends Component {
constructor(props){
super(props)
this.state = {
name: ''
}
}
handleChange(e) {
this.setState({
name: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
//PASS STATE TO PROFILE COMPONENT
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
ARMORY BETA
<input type="text" placeholder="Enter Name"
name="name"
value={this.state.name}
onChange={this.handleChange.bind(this)} />
<button className="btn btn-success" type="submit">Search</button>
</form>
</div>
)
}
}
export default SearchBar;
Profile
import React, { Component, PropTypes } from 'react';
import SearchBar from './SearchBar';
import ProfileContainer from '../containers/ProfileContainer';
class Profile extends Component {
render() {
return (
<div className="cols2">
<div>[IMG]</div>
<div>
<ProfileContainer name={this.props.name}/>
</div>
</div>
)
}
}
Profile.PropTypes = {
name: PropTypes.string
}
Profile.defaultProps = {
name: ''
}
export default Profile;
Main
import React, { Component } from 'react';
import TopNav from './TopNav';
import Footer from './Footer';
import Profile from './Profile';
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav />
</div>
<div className="row">
<Profile />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
export default Main;
In Main, you should add a prop to <TopNav /> that points to a handler method that will propagate the profileName state change back to Main. This, in turn, will cause Profile to be re-rendered. The handler method takes one argument profileName and is called from the handleSubmit method in TopNav. Here's the code:
SearchBar
class SearchBar extends Component {
. . .
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
this.props.handleProfileChange(profileName);
}
. . .
}
SearchBar.propTypes = {
handleProfileChange: React.PropTypes.func.isRequired,
}
Main
class Main extends Component {
constructor(props) {
super(props);
this.state = { profileName: '' }
handleProfileChange = this.handleProfileChange.bind(this);
}
handleProfileChange(profileName) {
// This state change will force Profile component to be re-rendered
this.setState( { profileName });
}
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav handleProfileChange={this.handleProfileChange} />
</div>
<div className="row">
<Profile profileName={this.state.profileName} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
You'll need to expose a property on SearchBar that accepts a callback that will be called to indicate to its parent that the form was submitted (e.g. onSubmit)...
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
//PASS STATE TO PROFILE COMPONENT
this.props.onSubmit(yourFormData);
}
...TopNav won't handle onSubmit itself, but just pass it on up to its own parent (perhaps renaming to "onSearchBarSubmit" along the way to make the name clearer from the perspective of TopNav's parent):
class TopNav extends Component {
render() {
return (
<div className="container-fluid">
<SearchBar onSubmit={this.props.onSearchBarSubmit}
</div>
);
}
}
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
<TopNav onSearchBarSubmit={ (criteria) => this.searchForStuff(criteria) } />
</div>
<div className="row">
<Profile data={this.state.stuffYouGotBackFromSearch} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
...OR, in some cases, it can be desirable to un-nest the components, allowing SearchBar as one of TopNav's props.children. This allows you to handle onSubmit directly within Main, and pass anything it receives onto Profile:
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav>
<SearchBar onSubmit={ (criteria) => this.searchForStuff(criteria) } />
</TopNav>
</div>
<div className="row">
<Profile data={this.state.stuffYouGotBackFromSearch} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
...a side-benefit of un-nesting is that it would allow you to use TopNav and Searchbar independently.

Resources