How can I use onClick on stateless component in React? - javascript

I want to use onClick on a stateless compoenent but it's reject an error like : onClick listener to be a function, instead got a value of object type.
I need to show and hide component on click.
Example when I click on the <ResultCard/> component I want to hide him and show <ResultDetail/>
State React Component :
import React, { Component } from "react";
import ResultCard from "./ResultCard";
import "../../assets/css/Result.css";
import Spinner from "../Spinner";
import { getApiToken, getParisByPrice } from "../../services/api";
import Modal from "../Modal";
import "../../assets/css/BudgetEntry.css";
import modify from "../../assets/images/modify.png";
import ResultDetail from "./ResultDetail";
class Results extends Component {
state = {
priceValue: "",
showResult: true
};
showResultDetail = () => {
this.setState({ showResult: false });
};
closeResultDetails = () => {
this.setState({ showResult: true });
};
render() {
return (
<div className="results-container">
{this.state.loading ? (
<Spinner />
) : (
<div className={"row"}>
{this.state.showResult ?
(
this.state.paris.map(details => (
<ResultCard
key={details.id}
id={details.id}
showResultDetail={this.showResultDetail}
prefix={details.prefix}
costPerDay={details.average_cost_per_day}
logoSports={details.infrastructure.map(home =>
home.logo_path.map(path_image => (
<img
src={path_image}
alt="icon-sports"
style={{ width: 20 }}
key={path_image}
/>
))
)}
/>
))
)
:
(
<ResultDetail closeResultDetail={this.closeResultDetails}/>
)
}
</div>
)}
</div>
);
}
}
export default Results;
ResultCard (who is stateless component):
import React from 'react';
import '../../assets/css/ResultCard.css';
const ResultCard = ({prefix, costPerDay, logoSports, showResultDetail, id}) => {
return (
<div className="card" onClick={showResultDetail} id={id}>
<p style={{margin:5}}>{prefix}</p>
<p style={{margin:1}}>arrondissement</p>
<p>{costPerDay} $</p>
{logoSports}
</div>
)
};
export default ResultCard;
ResultDetail (who is stateless component):
import React from 'react';
const ResultDetail = (closeResultDetail) => (
<div onClick={closeResultDetail}>
<p>Result detail</p>
</div>
)
export default ResultDetail;
thank for your help

The issue is here
const ResultDetail = (closeResultDetail) => (
You need to destructure it from the props object like this:
const ResultDetail = ({closeResultDetail}) => (
Or use it from props directly like this:
const ResultDetail = (props) => (
<div onClick={props.closeResultDetail}>
...

in your state component
showResultDetail = () => {
this.setState({ showResult: false });
};
render() {
....
<ResultCard
....
show={this.state.showResult}
//defer the execution of the method
onClick={(e) => this.showResultDetail(e)}/>
}
resultCard.js
const ResultCard = ({prefix, costPerDay, logoSports, showResultDetail, id, show}) => {
if(show)
return (
<div className="card" onClick={showResultDetail} id={id}>
<p style={{margin:5}}>{prefix}</p>
<p style={{margin:1}}>arrondissement</p>
<p>{costPerDay} $</p>
{logoSports}
</div>
);
};

in Results you define
showResultDetail = () => {
this.setState( {showResult: false });
};
as a function without arguments. You then pass
showResultDetail={(e) => this.showResultDetail(e)}
as a function with an event argument to your ResultCard. If you change that to
showResultDetail={this.showResultDetail}
your problem might already be fixed.
Edit: Here is a minimal snippet that does what you're looking for, I think.
const ResultCard = ({showResultDetail, show}) => {
return <div className="card" onClick={showResultDetail}>{show?'Click me!':''}</div>
};
class Results extends React.Component {
state = {
priceValue: "",
showResult: true
};
showResultDetail = () => {
this.setState({ showResult: false });
};
render() {
return <ResultCard show={this.state.showResult}
showResultDetail={this.showResultDetail}/>
}
}
ReactDOM.render(<Results/>, document.getElementById('root'))
.card {
width: 200px;
height: 50px;
background: lightgray;
text-align: center;
line-height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id='root'></div>

Related

How can I write callback func for setState on event click

after onclick event occurs in backpackList.js, fetch data in context.js and then through setState I want to update noneUserCart . After that i want to get data from context.js to backpackList.js to show web page. but the data is inital data []. How can I solve this problem?!
I think this is a Asynchronous problem, but I'm new react, so I don't know how to write code for this. or do I use async, await.
Help me please!
import React, { Component } from 'react';
const ProductContext = React.createContext();
const ProductConsumer = ProductContext.Consumer;
class ProductProvider extends Component {
constructor() {
super();
this.state = {
totalProducts: 0,
isLogin: false,
cartList: [],
isNavOpen: false,
isCartOpen: false,
noneUserCart: [],
};
}
noneUserAddCart = bagId => {
fetch('/data/getdata.json', {
method: 'GET',
})
.then(res => res.json())
.catch(err => console.log(err))
.then(data => {
this.setState(
{
noneUserCart: [...this.state.noneUserCart, data],
},
() => console.log(this.state.noneUserCart)
);
});
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleCart: this.handleCart,
getToken: this.getToken,
addNoneUserCart: this.addNoneUserCart,
hanldeCheckout: this.hanldeCheckout,
openNav: this.openNav,
showCart: this.showCart,
habdleCartLsit: this.habdleCartLsit,
deleteCart: this.deleteCart,
noneUserAddCart: this.noneUserAddCart,
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
export { ProductProvider, ProductConsumer };
import React, { Component } from 'react';
import { ProductConsumer } from '../../context';
export default class BackpackList extends Component {
render() {
const {
backpackdata,
backdescdata,
isdescOpen,
showDesc,
descClose,
rangenumone,
rangenumtwo,
} = this.props;
return (
<div>
{backdescdata.map((bag, inx) => {
return (
<>
{isdescOpen && bag.id > rangenumone && bag.id < rangenumtwo && (
<div className="listDescContainer" key={inx}>
<div className="listDescBox">
<ProductConsumer>
{value => (
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
</div>
)}
</ProductConsumer>
<span className="descClosebtn" onClick={descClose}>
X
</span>
</div>
</div>
</div>
)}
</>
);
})}
</div>
);
}
}
fetch is asynchronous, this.setState is yet called when console.log
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
{value.noneUserCart}
{/* when finished, result should show here */}
</div>

Meteor and react map returing undefined, I know the data is there but it loads, despite waiting for isLoading

I have the following code that passing leadsBuilder props to lead in the LeadBuilderSingle componenet. It has an array in a object and I access that array and try to map over it but it returns undefined. The data is being waited on and I am using isLoading, so I am not sure what is causing this error. It loads on first loading, but on page refresh gives me undefined.
import React, { useState, useEffect } from "react";
import Dasboard from "./Dashboard";
import { Container } from "../styles/Main";
import { LeadsBuilderCollection } from "../../api/LeadsCollection";
import { LeadBuilderSingle } from "../leads/LeadBuilderSingle";
import { useTracker } from "meteor/react-meteor-data";
const LeadCategoriesAdd = ({ params }) => {
const { leadsBuilder, isLoading } = useTracker(() => {
const noDataAvailable = { leadsBuilder: [] };
if (!Meteor.user()) {
return noDataAvailable;
}
const handler = Meteor.subscribe("leadsBuilder");
if (!handler.ready()) {
return { ...noDataAvailable, isLoading: true };
}
const leadsBuilder = LeadsBuilderCollection.findOne({ _id: params._id });
return { leadsBuilder };
});
return (
<Container>
<Dasboard />
<main className="">
{isLoading ? (
<div className="loading">loading...</div>
) : (
<>
<LeadBuilderSingle key={params._id} lead={leadsBuilder} />
</>
)}
</main>
</Container>
);
};
export default LeadCategoriesAdd;
import React from "react";
export const LeadBuilderSingle = ({ lead, onDeleteClick }) => {
console.log(lead);
return (
<>
<li>{lead.type}</li>
{lead.inputs.map((input, i) => {
return <p key={i}>{input.inputType}</p>;
})}
</>
);
};
FlowRouter.route("/leadCategories/:_id", {
name: "leadeBuilder",
action(params) {
mount(App, {
content: <LeadCategoriesAdd params={params} />,
});
},
});
try this :
lead.inputs && lead.inputs.map ((input, i) => {...}

React value returning as undefined

I am trying to map through the data in context and render out a list of cars. But I keep running into some sort of error. I would like to be able to select one of the cars and have it navigate me to another page that displays more details of the car. Current error is (TypeError: value.state.car is undefined). Any help would be great! Current code
Context.js
import React, {Component } from 'react';
import { carInventory } from './data';
export const MyContext = React.createContext();
export class MyProvider extends Component {
state = {
cars: []
};
componentDidMount() {
this.setCars();
}
setCars = () => {
let tempProducts = [];
carInventory.forEach(item =>{
const singleItem = {...item};
tempProducts = [...tempProducts,singleItem];
})
this.setState(()=>{
return {cars:tempProducts}
})
}
getItem = (id) => {
const car = this.state.cars.find(item => item.id === id);
return car;
}
handleDetail = (id) =>{
const car = this.getItem(id);
this.setState(()=>{
return {detailProduct:car}
})
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
handleDetail: this.handleDetail}}>
{this.props.children}
</MyContext.Provider>
)
}
}
VehicleList.js
import React, { Component } from "react"
import './App.css';
import Vehicles from "./Vehicles"
import { MyProvider, MyContext } from "./Context";
export default class VehicleList extends Component {
render() {
return (
<div className="vehicles">
<div className="showcase">
<MyContext.Consumer>
{(value) => (
<React.Fragment>
{value.state.car.map(car => {
return (
<Vehicles key={car.id} car=
{car} />
);
})}
</React.Fragment>
)}
</MyContext.Consumer>
</div>
</div>
)
}
}
import React, { Component } from 'react';
import './App.css';
import { MyProvider, MyContext } from "./Context";
import { Link } from 'react-router-dom';
class Vehicles extends Component {
render() {
const { make, model, id, info, img } = this.props.cars
return (
<div className="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<MyContext.Consumer>
{(value) => (<div className="img-container p-5"
onClick={() =>{
value.handleDetail(id)
}}>
<Link to="/VehicleOverview">
<img src={img} alt="product" className="card-img-top"/>
</Link>
</div>)}
</MyContext.Consumer>
</div>
</div>
)
}
}
export default Vehicles;
This is the kind of thing where static type checkers can really save you some grey hairs.
Your context indeed does not have any property car.
It does, however, have a property cars.
So you should map over cars, i.e. value.state.cars.map(car => ...)
:)

How to render the elements before to filter elements with ReactJS?

I'm doing a project which does a get of the json-server, and render them on the screen.
But when I added a filtering function on it, it only renders after I type a name to filter. I wanted him to render everyone and make the filter.
My Body.js (Where is my function of render):
import React from 'react';
import './Body.css';
import { Link } from "react-router-dom";
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "",
employeeBody: this.props.employee,
}
}
getName = () => {
const { employee, add } = this.props;
const {employeeBody} = this.state;
return employee.map(name => (
<div className='item'>
<Link className="link" to={`/user/${name.id}`}>
<div onClick={() => add(name)} key={name.id}>
<img className="img"
src={`https://picsum.photos/${name.id}`}
/>
</div>
<h1 className="name2"> {name.name} </h1>
</Link>
</div>
));
};
---
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
this.filterNames(inputValue);
console.log(this.state.employeeBody)
}
filterNames (inputValue) {
const { employee } = this.props;
this.setState({
employeeBody: employee.filter(item =>
item.name.includes(inputValue))
});
}
---
render() {
return (
<div>
<div className="body">
{this.getName()}
</div>
<div className='input'>
<input type="text" onChange={this.getValueInput} />
</div>
</div>
)
}
}
export default Body;
My App.js (Where i get the state by get of axios.):
import React from 'react';
import {
BrowserRouter as Router,
Route
} from "react-router-dom";
import './App.css';
import axios from 'axios';
import Body from './Body';
import User from './User';
import Header from './Header';
class AppRouter extends React.Component {
state = {
employeeCurrent: [],
employee: []
};
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({
employee: response.data
}));
}
add = name => {
this.setState(prevState => {
const copy = prevState.employeeCurrent.slice(1);
copy.push(name);
return {
employeeCurrent: copy
};
});
};
render() {
return ( <
Router >
<
div className = "router" >
<
Header / >
<
Route exact path = "/"
render = {
props => ( <
Body { ...props
}
add = {
this.add
}
employee = {
this.state.employee
}
employeeCurrent = {
this.state.employeeCurrent
}
/>
)
}
/> <
Route path = "/user/:id"
component = {
props => ( <
User { ...props
}
employee = {
this.state.employee
}
employeeCurrent = {
this.state.employeeCurrent
}
/>
)
}
/> <
/div> <
/Router>
);
}
}
export default AppRouter;
Someone would can help me ?
You should filter in the render method.
render() {
const { employee: employees } = this.props; // rename the variable {employee} to plural {employees}, it has more sense.
const { input } = this.state;
return (
<div>
<div className="body">
{employees
.filter(employee => employee.name.includes(input))
.map(employee => {
<div className='item'>
<Link className="link" to={`/user/${employee.id}`}>
<div onClick={() => add(employee)} key={employee.id}>
<img className="img"
src={`https://picsum.photos/${employee.id}`}
/>
</div>
<h1 className="name2"> {employee.name} </h1>
</Link>
</div>
})}
</div>
<div className='input'>
<input type="text" onChange={(e) => this.setState({ input: e.target.value })} />
</div>
</div>
);
}
Remember that the method includes is case sensitive, it should be lowerCase it before to compare.
P.S.: You could also create a variable / component / function and render split all the "logic" of rendering there.

button not working/function not executing

I want to post something(render on the screen) when I press a button.
Pressing the button doesn't give any errors but also doesn't give the result I expect from it. I'm new to react.
import React, { Component } from 'react';
import './style.css';
export default class Main extends Component {
newBlog = () => {
return (
<div>
<Post name = "James" about = "about java" post = "javs is great for complex software apps"/>
</div>
);
}
render() {
return(
<div>
<button onClick = {this.newBlog}>Post-Blog</button>
</div>
);
}
}
class Post extends Component {
render() {
const { name, about, post} = this.props;
return(
<div className = "main">
<h2>{name}</h2>
<h2>{about}</h2>
<hr/><br/>
<p>{post}</p>
</div>
);
}
}
As answer to your comment.
if i understand your point
import uuid from 'uuid/v4'; // get by npm install --save uuid || generate an unique id at each call
class Main extends React.Component {
constructor() {
super();
this.state = {
myBlog: [],
}
}
createNewBlog = () => {
var myPost = { name: 'John', about:'Javascript', post:"i'm john, i'll take about javascript"}
var currentMyBlog = this.state.myBlog;
currentMyBlog.push(myPost);
this.setState({myBlog: currentMyBlog});
};
render() {
return(
<div className=''>
{this.state.myBlog.map(post => (
<div key={uuid()}>
<Post name={post.name} about={post.about} post={post.post}/>
</div>
))}
<button onClick={ () => this.createNewBlog()}> Post blog</button>
</div>
)
}
};
The Returned JSX on button click needs to be rendered somewhere. You are better off setting a visible state on button click as onClick handler on Button doesn't do anything with the returned value in your case. Check the below snippet
class Main extends React.Component {
state = {visible: false}
newBlog = () => {
return (
<div>
<Post name = "James" about = "about java" post = "javs is great for complex software apps"/>
</div>
);
}
toggleVisible = () => {
this.setState(prev => ({visible: !prev.visible}))
}
render() {
return(
<div>
{this.state.visible? this.newBlog(): null}
<button onClick = {this.toggleVisible}>Post-Blog</button>
</div>
);
}
}
class Post extends React.Component {
render() {
const { name, about, post} = this.props;
return(
<div className = "main">
<h2>{name}</h2>
<h2>{about}</h2>
<hr/><br/>
<p>{post}</p>
</div>
);
}
}
ReactDOM.render(<Main/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"/>
you should use an arrow function with onClick event.
try that in render of Main component
render() {
return(
<div>
{this.state.visible? this.newBlog(): null}
<button onClick ={() => this.toggleVisible}>Post-Blog</button>
</div>
)
};

Categories