I am trying to update the state from an other component to an other component.
I want on header.jsx the state total to be updated when i click on add to cart button on product.jsx
Here is my code
index.jsx
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Header from './header';
import Footer from './footer';
import Posts from './posts';
import Post from './post';
import Products from './products';
import Product from './product';
import Page from './page';
// Load the Sass file
require('./style.scss');
const App = () => (
<div id="page-inner">
<Header />
<main id="content">
<Switch>
<Route exact path={Settings.path + 'products/:product'} component={Product} />
</Switch>
</main>
<Footer />
</div>
);
// Routes
const routes = (
<Router>
<Route path="/" component={App} />
</Router>
);
render(
(routes), document.getElementById('page')
);
header.jsx
import React from "react";
import { Link } from "react-router-dom";
class Header extends React.Component {
constructor(props) {
super(props);
this.state = { products: [], total: 0 }
var total = 0;
this.cartUpdated = this.cartUpdated.bind(this)
}
componentDidMount() {
//let cart = localStorage.getItem('total');
// this.setState({ total: 100 });
}
cartUpdated()
{
this.setState({ total: cart+100 });
}
render() {
return (
<div className="cart-icon p-3 m-auto">
Cart/ Total: <span className=""><span className="cart">€</span>{this.state.total}</span><i className="fas fa-shopping-cart" />
</div>
);
}
}
export default Header;
product.jsx
import React from "react";
import NotFound from "./not-found";
import "react-image-gallery/styles/scss/image-gallery.scss";
import ImageGallery from 'react-image-gallery';
class Product extends React.Component {
constructor(props) {
super(props);
this.state = { product: {}, total: 0
};
// ACTIONS
// addToCart
this.addToCart = this.addToCart.bind(this);
}
addToCart()
{
this.props.cartUpdated;
/*
let total = localStorage.getItem('total')
? JSON.parse(localStorage.getItem('total')) : {};
localStorage.setItem('total', 100); */
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
.......
};
renderProduct() {
if (this.state.product.images) {
const images = [];
if (this.state.product) {
this.state.product.images.map((image, i) => {
var new_image = {"original":image, "thumbnail":image} ;
images.push(new_image);
});
}
return (
<div className="col-md-12">
<div className="row">
<div className="col-md-6">
<ImageGallery items={images} showPlayButton={false} showFullscreenButton={false} thumbnailPosition="left" />
</div>
<div className="col-md-6">
<h4 className="card-title">{this.state.product.name}</h4>
<p className="card-text">
<strike>${this.state.product.regular_price}</strike>{" "}
<u>${this.state.product.sale_price}</u>
</p>
<p className="card-text">
<small className="text-muted">
{this.state.product.stock_quantity} in stock
</small>
</p>
<p
className="card-text"
dangerouslySetInnerHTML={{
__html: this.state.product.description
}}
/>
<div className="superflex add_to_cart_wrapper">
<button type="submit" name="add-to-cart" value={93} className="add_to_cart btn btn-success alt" onClick={this.props.cartUpdated}>Add to cart</button>
</div>
</div>
</div>
</div>
);
}
}
renderEmpty() {
return <NotFound />;
}
render() {
return (
<div className="container post-entry">
{this.state.product ? this.renderProduct() : this.renderEmpty()}
</div>
);
}
}
export default Product;
You can do something like this to achieve this. But the more clean solution which is suggested by the React JS is to use the React Context API.
Am sharing a link from the React JS documentation which exactly have the same scenario that you want to tackle.
https://reactjs.org/docs/context.html#updating-context-from-a-nested-component
And also since you are using the React pure component function so we can use the React hooks, you can have a look here at
https://reactjs.org/docs/hooks-reference.html#usestate
so in your code it should be like this
./Total-Context.js
export const TotalContext = React.createContext({
total: 0,
setTotal: () => {
},
});
./index.jsx
import { TotalContext } from './Total-Context';
const App = () => {
const [total, setTotal] = useState(0);
return (
<TotalContext.Provider value={{total, setTotal}}>
<div id="page-inner">
<Header currentTotal={total} />
<main id="content">
<Switch>
<Route
exact
path={`${Settings.path}products/:product`}
component={Product}
/>
</Switch>
</main>
<Footer />
</div>
</TotalContext.Provider>
);
};
and Now we can use the TotalContext consumer in the Product component and call the method to set the total method in the global context like this.
./Product.jsx
import { TotalContext } from './Total-Context';
const Product = () => (
<TotalContext.Consumer>
{({total, setTotal}) => (
<button
onClick={() => {setTotal(newTotal)}}
>
Update total
</button>
)}
</TotalContext.Consumer>
)
so after calling the click method the Header component should have the updated value of the total.
you can use redux to manage state across multiple components.
getting started with redux
To Answer this:
There is no way by the help of which you can pass state between two react components, as state is private to a component.
props can help you in this regard. Props also can't be passed from child to parent it can always be from parent to child.
There is a twist by the help of which you can achieve this, please follow the below article section: "How to pass Props from child to parent Component?" to get clear idea on this:
URL: https://www.robinwieruch.de/react-pass-props-to-component/
Related
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"));
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.
so I've recently completed a project that I was working on that displayed a list of pokemon, and once clicked on, the user is directed to the pokemon information page.
It looks as follows:
So I have my main dashboard.js that contains my "PokemonList" as follows:
import React, { Component } from "react";
import styled from "styled-components";
import PokemonList from "../pokemon/PokemonList";
export default class Dashboard extends Component {
render() {
return (
<div>
<div className="row">
<div className="col">
<PokemonList />
</div>
</div>
</div>
);
}
}
my PokemonList.js is responsible for obtaining the Pokemon information from the PokeAPI and the code is as follows:
import React, { Component } from "react";
import PokemonCard from "./PokemonCard";
import axios from "axios";
export default class PokemonList extends Component {
state = {
url: "http://pokeapi.co/api/v2/pokemon/?limit=600",
pokemon: null
};
async componentDidMount() {
const res = await axios.get(this.state.url);
this.setState({ pokemon: res.data["results"] });
}
render() {
return (
<React.Fragment>
{this.state.pokemon ? (
<div className="row">
{this.state.pokemon.map(pokemon => (
<PokemonCard
key={pokemon.name}
name={pokemon.name}
url={pokemon.url}
/>
))}
</div>
) : (
<h1>Loading Pokemon</h1>
)}
</React.Fragment>
);
}
}
The pokemonList is built of several pokemonCards that's then displayed, but I don't think the coding for that is needed for what I'm looking for.
If I wanted to enable pagination, would I have to incorporate the code within my Dashboard.js or the pokemonList.js?
-----------------------EDIT--------------------------------------
What you could use is this library: https://www.npmjs.com/package/react-js-pagination
Then in your code the pagination would be smth like this:
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={this.state.itemsCountPerPage}
totalItemsCount={this.state.pokemon.length}
pageRangeDisplayed={5}
onChange={::this.handlePageChange.bind(this)}
/>
handlePageChange function:
handlePageChange(pageNumber) {
this.setState({activePage: pageNumber});
}
then in the render function of your pokemonList.js:
let indexOfLastTodo = this.state.activePage * this.state.itemsCountPerPage;
let indexOfFirstTodo = indexOfLastTodo - this.state.itemsCountPerPage;
let renderedPokemons = this.state.pokemon.slice(indexOfFirstTodo, indexOfLastTodo);
and finally
{renderedPokemons.map(pokemon => (
<PokemonCard
key={pokemon.name}
name={pokemon.name}
url={pokemon.url}
/>
))}
Of course don't forget to include activePage and itemsCountPerPage in your state. I think I have done something like this in one of my earlier projects. Enjoy!
So I'm trying to break the component on my App.js into a smaller component, that being my Sidebar.js. I took a small section of the code and put it in its own Sidebar.js file but no matter what I've tried, I cant call my function getNotesRows() from App.js without it being unable to find it or this.states.notes being undefined.
I just want it to send the code back and forth. This is a demo app, so I know it's not the most practical.
import React, { Component } from "react";
import classNames from "classnames";
import logo from "./logo.svg";
import checkMark from "./check-mark.svg";
import "./App.css";
import Sidebar from "./components/Sidebar.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
notes: [],
currentNoteIndex: 0
};
this.markAsRead = this.markAsRead.bind(this);
this.selectNote = this.selectNote.bind(this);
console.log("Test started 2.25.19 19:23");
}
componentWillMount() {
fetch('/notes')
.then(response => response.json())
.then(
notes => {
this.setState({
notes: notes,
currentNoteIndex: 0
})
}
)
.catch(
error => {
console.log('Ooops!');
console.log(error);
}
);
}
markAsRead() {
this.setState(currentState => {
let marked = {
...currentState.notes[currentState.currentNoteIndex],
read: true
};
let notes = [...currentState.notes];
notes[currentState.currentNoteIndex] = marked;
return { ...currentState, notes };
});
}
selectNote(e) {
this.setState({ currentNoteIndex: parseInt(e.currentTarget.id, 10) });
}
getTotalUnread() {
let unreadArray = this.state.notes.filter(note => {
return note.read === false;
})
return unreadArray.length;
}
getNotesRows() {
return this.props.notes.map(note => (
<div
key={note.subject}
className={classNames("NotesSidebarItem", {
selected:
this.props.notes.indexOf(note) === this.props.currentNoteIndex
})}
onClick={this.selectNote}
id={this.props.notes.indexOf(note)}
>
<h4 className="NotesSidebarItem-title">{note.subject}</h4>
{note.read && <img alt="Check Mark" src={checkMark} />}
</div>
));
}
// TODO this component should be broken into separate components.
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Notes Viewer Test App</h1>
<div>
Unread:
<span className="App-title-unread-count">
{this.getTotalUnread()}
</span>
</div>
</header>
<div className="Container">
<Sidebar />
<section className="NoteDetails">
{this.state.notes.length > 0 && (
<h3 className="NoteDetails-title">
{this.state.notes[this.state.currentNoteIndex].subject}
</h3>
)}
{this.state.notes.length > 0 && (
<p className="NoteDetails-subject">
{this.state.notes[this.state.currentNoteIndex].body}
</p>
)}
{this.state.notes.length > 0 && (
<button onClick={this.markAsRead}>Mark as read</button>
)}
{this.state.notes.length <= 0 && (
<p>
No Notes!
</p>
)}
</section>
</div>
</div>
);
}
}
export default App;
Above is my App.js
and below is the Sidebar.js that I'm trying to create
import React, { Component } from "react";
import "../App.css";
import App from "../App.js";
class Sidebar extends React.Component{
constructor(props) {
super(props);
}
render(){
return (
<section className="NotesSidebar">
<h2 className="NotesSidebar-title">Available Notes:</h2>
<div className="NotesSidebar-list">{App.getNotesRows()}</div>
</section>
)}}
export default Sidebar;
You cannot access a method like that. You need to pass the method as a prop and use it in the child.
<Sidebar getNotesRows={this.getNotesRows} />
and in Sidebar use
<div className="NotesSidebar-list">{this.props.getNotesRows()}</div>
In your sidebar, you're trying to call getNotesRows() from App, but Sidebar doesn't need access to app (you shouldn't have to import App in Sidebar.js). Instead, you should pass the function from App to your Sidebar component, and reference it from Sidebar's props.
In App.js, you'll need to bind getNotesRows and pass it to sidebar.:
<Sidebar getNotesRows={ this.getNotesRows } />
Then in Sidebar.js, you'll need to reference getNotesRows in your render method:
render() {
const notes = this.props.getNotesRows();
return (
<section className="NotesSidebar">
<h2 className="NotesSidebar-title">Available Notes:</h2>
<div className="NotesSidebar-list">{ notes }</div>
</section>
);
}
It seems like the problem here is that you are trying to use a class function as a static property, to put it simply, you have not initialized the App class when you import it into your sidebar(?), thus no static function was found on your App class so you can call App.getNotesRows() maybe you should re-think your components and separate them in container-components using a Composition Based Programming approach instead of OO approach.
I am developing UI for a search engine using React. For Searching, I am using Elasticsearch. I am able to connect and fetch results from my index, but I'm only able to fetch only those number of results of which size i specified. I want to enable Infinite scroll until the client reaches to the end of results.
I need help in implementing scan & scroll and pagination
Here is my code of app.js component
import React from 'react';
import Header from './header';
import Footer from './footer';
import SearchResults from './searchresults';
import elasticsearch from 'elasticsearch';
let client = new elasticsearch.Client({
host: 'localhost:9200',
log: 'trace'
})
class App extends React.Component {
constructor(props) {
super(props);
this.state = { results: [], search_query: '*' }
this.handleChange = this.handleChange.bind(this);
}
handleChange ( event ) {
var search_query = event.target.value;
var size = 20;
client.search({
index: 'photos',
type: 'photo',
q: search_query,
size: size
}).then(function ( body ) {
this.setState({ results: body.hits.hits })
}.bind(this), function ( error ) {
console.trace( error.message );
});
}
render () {
return (
<div className="main">
<Header />
<div className="row test">
<div className="col-xs-4"></div>
<div className="col-xs-4">
<div className="row">
<div className="col-xs-12">
<input id="search" className="form-control" type="text" placeholder="Start Searching" name="search" onChange={ this.handleChange }></input>
</div>
</div>
</div>
<div className="col-xs-4"></div>
<br />
</div>
<div className="test bclass">
<SearchResults results={ this.state.results } />
<button type="button" className="btn btn-default">Load More</button>
</div>
<Footer />
</div>
)
}
}
export default App;
And here is my code for searchresults.js component:
import React from 'react';
import PropTypes from 'prop-types';
import LazyLoad from 'react-lazy-load';
let i = 1;
class SearchResults extends React.Component {
constructor(props) {
super(props);
this.state = { results: [] }
}
render () {
return (
<div className="search_results">
<hr />
<ul>
{ this.props.results.map((result) => {
return (
<li key={ result._id + i++}>
<LazyLoad className="lazy">
<img className="image" src={result._source.file_name} alt="Search Result" />
</LazyLoad>
</li>
) }) }
</ul>
</div>
)
}
}
SearchResults.propTypes = {
results: PropTypes.array
}
export default SearchResults;
Make use of AJAX call on scroll as in Facebook home page; use a global incrementing counterVariable that increments on each AJAX call for your ElasticSearch with parameters from records of your result documents of size you want in one AJAX call of scroll. Do increment on every ajax call.
For javaScript of calling AJAX on scroll refer Similar Solved Answer .