lifting up 3 level component - javascript

I want to do a toggle for the search bar. When I clicked the searchIcon, the searchBar will show or hide. However, i need to lifting up 3 level parent and child. How can I pass the onClick to do the toggle?
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activities: activities,
filteredActivities: activities,
};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
filterActivity = searchText => {
//
}
handleSearchChange = inputValue => {
//
};
render() {
const filteredActivities = this.props.filteredActivities;
return(
<div className="notificationsFrame">
<div className="panel">
<Header name={this.props.name} />
<SearchBar inputChanged={this.handleSearchChange} />
<Content activities={this.state.filteredActivities} />
</div>
</div>
);
}
}
class Header extends React.Component {
render() {
return (
<div className="header">
<MenuIcon />
<Title name={this.props.name} />
<SearchIcon />
</div>
);
}
}
class SearchIcon extends React.Component {
render() {
return <div className="fa fa-search searchIcon" onClick={}></div>;
}
}

onClick={this.props.onClick}
or
{...props}
Full code:
import React from "react";
import "./styles.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
handleSearchChange = inputValue => {
console.log("test");
};
render() {
return (
<div className="notificationsFrame">
<div className="panel">
<Header name={this.props.name} onClick={this.handleSearchChange} />
</div>
</div>
);
}
}
class Header extends React.Component {
render() {
return (
<div className="header">
<SearchIcon onClick={this.props.onClick} />
</div>
);
}
}
class SearchIcon extends React.Component {
render() {
return (
<div className="fa fa-search searchIcon" {...this.props}>
XXX
</div>
);
}
}

Related

TypeError: _this.props.onUpdateShelf is not a function | React | Classbased component

I tried this.bind(this), but still get the same error. When I tried to move book from one to another shelf, this error occurred.
This is my code and in handleShelfChange (line-11) got the error saying:
onUpdateShelf is not a function.
import { Component } from 'react';
import React from 'react'
class Book extends Component {
state={
value: ''
};
handleShelfChange= event => {
const newValue=event.target.value;
this.setState({value:newValue},() =>{
this.props.onUpdateShelf(this.props.book,newValue)
});
}
render() {
const{book,title,UpdateShelf}=this.props;
return(
<li>
<div className="book">
<div className="book-top">
<div className="book-cover" style={{ width: 128, height: 193,
backgroundImage: 'url("http://books.google.com/books/content?id=PGR2AwAAQBAJ&printsec=frontcover&img=1&zoom=1&imgtk=AFLRE73-GnPVEyb7MOCxDzOYF1PTQRuf6nCss9LMNOSWBpxBrz8Pm2_mFtWMMg_Y1dx92HT7cUoQBeSWjs3oEztBVhUeDFQX6-tWlWz1-feexS0mlJPjotcwFqAg6hBYDXuK_bkyHD-y&source=gbs_api")' }}></div>
<div className="book-shelf-changer">
<select
value={this.state.value}
onChange={this.handleShelfChange.bind(this)} >
<option value="move" disabled>Move to...</option>
<option value="currentlyReading">Currently Reading</option>
<option value="wantToRead">Want to Read</option>
<option value="read">Read</option>
<option value="none">None</option>
</select>
</div>
</div>
<div className="book-title">{book.title}</div>
<div className="book-authors">{book.authors}</div>
</div>
</li>
)
}
}
export default Book;
this is the parent component
import React from 'react'
import * as BooksAPI from './BooksAPI'
import './App.css'
import BookShelf from './BookShelf'
//import Book from './Book'
import SearchPage from './SearchPage'
import { Route } from 'react-router-dom'
import { Link } from 'react-router-dom'
class BooksApp extends React.Component {
state = {
readBooks:[]
}
componentDidMount() {
BooksAPI.getAll().then(books => {
this.setState({readBooks:books})
})
}
UpdateShelf = (book,shelf) => {
BooksAPI.update(book,shelf).then(books => {
book.shelf = shelf;
})
let updateBook = (book) =>{
this.setState(currState => ({
readBooks: currState.book.filter(b=>
b.id !== book.id
)
}));
}
}
render() {
const{book,onUpdateShelf}=this.props;
return (
<div className="app">
<Route exact path="/search"
render={() =>(
<SearchPage
book={book}
onUpdateShelf={onUpdateShelf} />
)}/>
<Route exact path="/"
render={() =>(
<div className="list-books">
<div className="list-books-title">
<h1>MyReads</h1>
</div>
<div className="list-books-content">
<BookShelf shelfType = "currentlyReading"
onUpdateShelf={onUpdateShelf}
book={this.state.readBooks.filter(b =>
b.shelf === "currentlyReading"
)} />
<BookShelf shelfType = "wantToRead"
onUpdateShelf={onUpdateShelf}
book={this.state.readBooks.filter(b =>
b.shelf === "wantToRead"
)} />
<BookShelf shelfType = "Read"
onUpdateShelf={onUpdateShelf}
book={this.state.readBooks.filter(b =>
b.shelf === "read"
)} />
</div>
<div className ="open-search">
<Link to="/search">
<button>Search Books</button>
</Link>
</div>
</div>
)} />
</div>
);
}
}
export default BooksApp;
also i attached the bookshelf component
import { Component } from 'react';
import React from 'react'
import Book from './Book'
class BookShelf extends Component {
render() {
const{onUpdateShelf,shelfType}=this.props;
return(
<div className="bookshelf">
<h2 className="bookshelf-title">{shelfType}</h2>
<div className="bookshelf-books">
<ol className="books-grid">
{this.props.book.map((book,key) =>
<Book
key={key}
book={book}
onUpdateShelf={onUpdateShelf} />
)}
</ol>
</div></div>
)
}
}
export default BookShelf;
you need to take one argument props to your class. then only you can access the values and functions in our class
class Book extends Component (props){
state={
change yor code like this

Opposite react component state changing

I have a simple login form component that when I click, would like for the form to disappear and only display my json. I am a little rusty with working with react state, and appear to have the opposite effect of what I am trying. When I click on my button event, the json I am displaying will toggle appearing and disappearing, but the form stays static. I need the form to disappear and the page to populate with my grid.
Here is my components
index.jsx
import React from 'react';
import SignUp from '../SignUp';
import Cards from '../Articles/Cards';
export default class Gecko extends React.Component {
constructor(props) {
super(props);
this.state = { requestedPostsThatWeGotFromGecko: null, }
this.clickMe = this.clickMe.bind(this)
}
clickMe = () => {
const {requestedPostsThatWeGotFromGecko} = this.state;
this.setState({ requestedPostsThatWeGotFromGecko: !requestedPostsThatWeGotFromGecko })
}
render() {
console.log(this.state);
return (
<div className='gecko'>
<SignUp login={() => this.clickMe()}/>
{this.state.requestedPostsThatWeGotFromGecko &&
<Cards />
}
</div>
);
}
}
Sign up component
import React from 'react';
export default class SignUp extends React.Component {
render() {
const onClick = () => {
this.props.login();
console.log('rich');
}
return (
<div className='sign-up'>
<table className='sign-up-form'>
<tbody>
<div class="gecko-signup__tabs"><button id="gecko-signup" data-selected="yes">Sign Up</button><button id="gecko-login" data-selected="">Log In</button></div>
<tr>
<td>
<p id="signUpFree">Sign Up for Free</p>
</td>
</tr>
<div id="inputs-section">
<tr>
<td><input id="first" placeholder="First Name*" /></td>
<td><input id="last" placeholder="Last Name*" /></td>
</tr>
</div>
<tr>
<td colSpan="2"><input placeholder="Email Address*" /></td>
</tr>
<tr>
<td colSpan="2"><input placeholder="Set A Password*" /></td>
</tr>
<tr>
<td colSpan="2"><input id="getStarted" type="submit" value="Get Started" onClick={onClick}/></td>
</tr>
</tbody>
</table>
</div>
);
}
}
CardSetup component
import React from 'react';
import SignUp from '../SignUp';
export default class Articles extends React.Component {
constructor(props) {
super(props);
this.state = {
requestedPostsThatWeGotFromGecko: [],
}
}
componentDidMount(){
const api = 'https://5d445466d823c30014771642.mockapi.io/api/v1/products';
const request = new Request(api);
// Fetch isn't browser compatible...Might should fix.
fetch(request)
.then(response => {
if (response.status === 200) {
return response.json();
} else {
throw new Error('Something went wrong on api server!');
};
}).then(response => {
this.setState({
requestedPostsThatWeGotFromGecko: response
});
})
.catch(error => {
console.error(error);
});
}
render() {
return(
<div className='articles'>
{this.state.requestedPostsThatWeGotFromGecko.map(product => {
return (
<div className='flex-grid'>
<div className="card">
<div className="overflow">
<img className='productImage' src={product.image}></img>
</div>
<div className='card-body'>
<p id='name'>{product.name}</p>
<p id='description'>{product.description}</p>
<p id='price'>{product.price} </p>
</div>
</div>
</div>
);
})
}
</div>
)}}
Final Cards component
import React from 'react';
import Articles from './CardSetup';
export default class Cards extends React.Component {
render() {
return(
<div className="cards">
<h2>Products</h2>
<div className="column">
<Articles />
</div>
<div className="column">
<Articles />
</div>
<div className="column">
<Articles />
</div>
<div className="column">
<Articles />
</div>
</div>
);
}
}
I am pretty sure that I am setting the state incorrectly somewhere along the line after I press the button. I am thinking about jquery and wanting to "hide" the element but I know that is incorrect with react. Any help is greatly appreciated.
Conditionally render Cards or Signup based on truthy/falsey value of requestedPostsThatWeGotFromGecko.
render() {
const { requestedPostsThatWeGotFromGecko } = this.state;
return (
<div className="gecko">
{requestedPostsThatWeGotFromGecko ? (
<Cards />
) : (
<SignUp login={() => this.clickMe()} />
)}
</div>
);
}
Probably this is what you want:
render() {
return (
<div className='gecko'>
{!this.state.requestedPostsThatWeGotFromGecko &&
<SignUp login={() => this.clickMe()}/>
}
{this.state.requestedPostsThatWeGotFromGecko &&
<Cards />
}
</div>
);
}
If I understood correctly, you want to toggle between the Signup form and Cards based on requestedPostsThatWeGotFromGecko state variable.
So you can do something like this in your index.jsx:
render() {
return (
<div className='gecko'>
{this.state.requestedPostsThatWeGotFromGecko ?
<Cards /> :
<SignUp login={() => this.clickMe()} />
}
</div>
);
}
All you have to do is conditionally render the SignUp page on the basis of flag requestedPostsThatWeGotFromGecko.
Note: Important thing is you have to initialize it with false and make it true on the click from the SignUp page.
constructor(props) {
super(props);
this.state = { requestedPostsThatWeGotFromGecko: false };
this.clickMe = this.clickMe.bind(this)
}
render() {
const { requestedPostsThatWeGotFromGecko } = this.state;
return (
<div className="gecko">
{requestedPostsThatWeGotFromGecko ? (
<Cards />
) : (
<SignUp login={() => this.setState({ requestedPostsThatWeGotFromGecko: true })} />
)}
</div>
);
}

React onClick element: call another class elements

I am trying to achieve a modal popup(which is stored in postview class) to appear when a onclick is called in any post on postcontainer class. I am new to react, so I would love your suggestion on improving code.
Class 1 (PostCointainer)
This is main class which shows multiple post from an array. I want the modal to appear from class 2 when any post is clicked
import React, { Component, useState } from 'react';
import '../App.css';
import PostView from './PostView';
function RenderPost({posts}) {
return (
<div className="post-holder shadow-sm p-3 mb-4 bg-white rounded" key={posts.id} onClick={class postview class 2}>
</div>
);
}
const PostContainer = props => {
const menu = props.posts.map(post => {
return (
<RenderPost posts={post} />
)
});
return (
<div className="container-fluid">
<div className="row justify-content-center">
<PostView />
{menu}
</div>
</div>
);
}
export default PostContainer;
Class 2 (post View)
class PostView extends Component {
constructor(props) {
super(props);
this.state = {
isModalOpen : true,
}
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal(e) {
e.stopPropagation()
this.setState({
isModalOpen: !this.state.isModalOpen
});
}
render() {
return (
<div className="shadow-sm p-2 mb-2 bg-white">
<div
className="modal"
style={{display: this.state.isModalOpen ? 'block' : 'none' }}
onClick={this.toggleModal} >
<div
className="modal-content"
onClick={ e => e.stopPropagation() } >
<span
className="close"
onClick={this.toggleModal}
>×
</span>
<div className="container">
<h3 className="form-header">Hello</h3>
</div>
</div>
</div>
</div>
);
}
}
export default PostView;
This can be simply acheived by maintaining a state variabe for post click in the parent component and passing it via prop to child.
PostContainer.js
import React, { Component, useState } from 'react';
import '../App.css';
import PostView from './PostView';
const PostContainer = props => {
const [post, setPost] = useState(false);
function RenderPost({posts}) {
return (
<div className="post-holder shadow-sm p-3 mb-4 bg-white rounded" key={posts.id} onClick={setPost(posts)}>
</div>
);
}
const menu = props.posts.map(post => {
return (
<RenderPost posts={post} />
)
});
return (
<div className="container-fluid">
<div className="row justify-content-center">
<PostView post={post} />
{menu}
</div>
</div>
);
}
export default PostContainer;
Constructor function of PostView Component
constructor(props) {
super(props);
this.state = {
isModalOpen : !!props.post,
}
this.toggleModal = this.toggleModal.bind(this);
}
And you can use the same post prop to render post in render function of your child component.
Hope it helps!!

Changing state of parent element in Reactjs resetting state of all <Route /> based components elements?

Basically changing <Parent /> is resetting children state. Only for components attached to <Route />
I was able to duplicate issue in codepen
https://codepen.io/anon/pen/BZVdzE
var BrowserRouter =ReactRouterDOM.BrowserRouter;
var Route =ReactRouterDOM.Route;
var Link = ReactRouterDOM.Link;
class Parent extends React.Component {
constructor() {
super();
this.state = {
title: "blank"
};
}
changeText(x) {
this.setState({ title: x });
}
render() {
return (
<div>
<div className="btn" onClick={this.changeText.bind(this, 1)}>1</div>
<div className="btn" onClick={this.changeText.bind(this, 2)}>>2</div>
<div className="btn" onClick={this.changeText.bind(this, 3)}>>3</div>
<p> {this.state.title}</p>
<BrowserRouter>
<Route path='/' component={(props) => <Child {...props} state={this.state} />} />
</ BrowserRouter>
</div>
);
}
}
class Child extends React.Component {
constructor() {
super();
this.state = {
favoriteColor: "none choosen"
};
}
changeColor(color) {
this.setState({favoriteColor: color});
}
render() {
return (
<div>
<div className="btn" onClick={this.changeColor.bind(this, 'Green')}>Green</div>
<div className="btn" onClick={this.changeColor.bind(this, 'Purple')}>Purple</div>
<div className="btn" onClick={this.changeColor.bind(this, 'Brown')}>Brown</div>
<p> {this.state.favoriteColor} </p>
</div>
);
}
}
ReactDOM.render(<Parent />, document.getElementById('app'));
Choose favorite color, change number, favorite color goes to default value.

Passing props from child to parent(without context)

I'm having a little bit of problem with wrapping my head around with passing states into parents. I need to send data from form container to app so that I can show updated states of list in weather info after submit
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Weather App</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<FormContainer label="Name of the city:"/>
<WeatherInfo
nameOfCity={this.state.nameOfCity}
weatherDescription={this.state.weatherDescription}
windSpeed={this.state.windSpeed}
temperature={this.state.temperature}
maxTemperature={this.state.maxTemperature}
minTemperature={this.state.minTemperature}
/>
</div>
);
}
}
export default App;
Form Container
class FormContainer extends Component {
constructor(props) {
super(props);
this.state = {
cityName: '',
nameOfCity:'',
weatherDescription:'',
windSpeed:'',
temperature:'',
maxTemperature:'',
minTemperature:''
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleCityName = this.handleCityName.bind(this);
}
handleFormSubmit(e) {
e.preventDefault();
const SendForm = {
cityName: this.state.cityName
};
console.log(SendForm);
fetch(`http://api.openweathermap.org/data/2.5/forecast/weather?q=${SendForm.cityName}&units=metric&APPID=********`)
.then(res => res.json())
.then(results => {
this.setState({
nameOfCity: results.city.name,
weatherDescription: results.list[0].weather[0].description,
windSpeed: results.list[2].wind.speed,
temperature: results.list[0].main.temp,
maxTemperature: results.list[0].main.temp_max,
minTemperature: results.list[0].main.temp_min
});
});
}
handleCityName(value) {
this.setState({ cityName: value });
}
render() {
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<label>{this.props.label}</label>
<SearchBar
name="CityName"
type="text"
value={this.state.cityName}
placeholder="search"
onChange={this.handleCityName}
/>
<button type="submit"
className=""
value='Submit'
placeholder="Search" />
</form>
</div>
);
}
}
export {FormContainer};
Search bar component
const SearchBar = (props) => (
<div>
<label>{props.label}</label>
<input name={props.name} type={props.inputType} value={props.value} placeholder={props.placeholder} onChange={(e)=>props.onChange(e.target.value)}/>
</div>
);
export default SearchBar;
and Weather Info component
const WeatherInfo = (props) => (
<div>
<ul>
<li>{props.nameOfCity}</li>
<li>{props.weatherDescription}</li>
<li>{props.windSpeed}</li>
<li>{props.temperature}</li>
<li>{props.maxTemperature}</li>
<li>{props.minTemperature}</li>
</ul>
</div>
);
export default WeatherInfo;
You can pass method to update App state to FormContainer component
class App extends Component {
constructor() {
this.state = {
cityName: '',
nameOfCity:'',
weatherDescription:'',
windSpeed:'',
temperature:'',
maxTemperature:'',
minTemperature:''
};
}
updateInfo(results) {
this.setState({
nameOfCity: results.city.name,
weatherDescription: results.list[0].weather[0].description,
windSpeed: results.list[2].wind.speed,
temperature: results.list[0].main.temp,
maxTemperature: results.list[0].main.temp_max,
minTemperature: results.list[0].main.temp_min
});
}
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Weather App</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<FormContainer label="Name of the city:" updateInfo={this.updateInfo.bind(this)}
nameOfCity={this.state.nameOfCity}
/>
<WeatherInfo
nameOfCity={this.state.nameOfCity}
weatherDescription={this.state.weatherDescription}
windSpeed={this.state.windSpeed}
temperature={this.state.temperature}
maxTemperature={this.state.maxTemperature}
minTemperature={this.state.minTemperature}
/>
</div>
);
}
}
export default App;
And call it from FormComponent
class FormContainer extends Component {
constructor(props) {
super(props);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleCityName = this.handleCityName.bind(this);
}
handleFormSubmit(e) {
e.preventDefault();
const SendForm = {
cityName: this.props.cityName
};
console.log(SendForm);
fetch(`http://api.openweathermap.org/data/2.5/forecast/weather?q=${SendForm.cityName}&units=metric&APPID=********`)
.then(res => res.json())
.then(results => {
this.props.updateInfo(results);
});
}
handleCityName(value) {
// Do what you want to do, like resend API request or smth
}
render() {
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<label>{this.props.label}</label>
<SearchBar
name="CityName"
type="text"
value={this.props.cityName}
placeholder="search"
onChange={this.handleCityName}
/>
<button type="submit"
className=""
value='Submit'
placeholder="Search" />
</form>
</div>
);
}
}
export {FormContainer};

Categories