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
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
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>
);
}
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!!
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.
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};