How can I write callback func for setState on event click - javascript

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>

Related

How to render an asynchronous result (array) in a component in react?

I have been doing js for about a month now, and I am writing this program where I am using clarifai API to see which celebrity a person on the photo resembles the most.
I want to pass the output as props to Rank component to render it, but
I get the
Type error: clarifaiResults.map is not a function at App.transformResponse
Basically, the response I want to pass as props is the
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
part that I get in console.log now
I am assuming it's because there is no output yet when the app tries to render the component, but I can't figure out what's wrong with the code. Thank you!
App.js
import React, { Component } from 'react';
import './App.css';
import SignIn from './Component/SignIn/SignIn.js';
import Register from './Component/Register/Register.js';
import Particles from 'react-particles-js';
import Logo from './Component/Logo/Logo.js';
import Navigation from './Component/Navi/Navigation.js';
import ImageLinkForm from './Component/Form/ImageLinkForm.js';
import Rank from './Component/Rank/Rank.js'
import Clarifai from 'clarifai';
import FaceRecognition from './Component/Face/FaceRecognition.js';
import FaceComparison from './Component/Comparison/FaceComparison.js';
const app = new Clarifai.App({
apiKey: 'MYSUPERSECRETKEY'
});
const initialState = {
input: "",
imageUrl: "",
results: [],
route: "SignIn",
user: {
id: "",
name: "",
email: "",
entries: 0,
joined: "",
},
};
const particleOptions = {
particles: {
number: {
value: 40,
density: {
enable: true,
value_area: 800,
},
}
}
}
class App extends Component{
constructor() {
super();
this.state = initialState;
}
transformResponse = (response) => {
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
const results = clarifaiResults.map((ingredient) => ({
ingredients: ingredient.name,
probablitiy: ingredient.value,
}));
this.setState({results: results.celebrityName});
return {results: []};
};
onInputChange = (event) => {
this.setState({input: event.target.value});
}
onSubmit = () => {
this.setState({imageUrl: this.state.input});
app.models
.predict(
Clarifai.CELEBRITY_MODEL,
this.state.input)
.then(response => {
console.log(response.outputs[0].data.regions[0].data.concepts[0].name)
if (response) {
fetch ('http://loclhost:3000', {
method: 'post',
headers: {'Conent-Type' : 'application/json'},
body: JSON.stringify({
input: this.state.user.input
})
})
.then((response) => response.json())
.then(count => {
this.setState(Object.assign(this.state.user, {entries:count}))
})
}
this.transformResponse(response);
})
.catch(err => console.log(err));
};
;
onRouteChange = (route) => {
if (route === 'signout'){
this.setState({isSignedIn: false})
} else if (route ==='home'){
this.setState({isSignedIn: true})
}
this.setState({route: route});
}
render() {
let { isSignedIn, imageUrl, route, results} = this.state;
return (
<div className="App">
<Particles className='particles'
params={particleOptions}
/>
<Navigation isSignedIn={isSignedIn} onRouteChange={this.onRouteChange}/>
{ route ==='home'
? <div>
<Logo />
<Rank
results = {results}/>
<ImageLinkForm
onInputChange={this.onInputChange}
onSubmit={this.onSubmit}
/>
<FaceRecognition
imageUrl={imageUrl}
/>
<FaceComparison
results = {results}
/>
</div>
: (
route === 'SignIn'
? <SignIn onRouteChange={this.onRouteChange}/>
: <Register />
)
}
</div>
);
};
}
export default App;
Rank.js
import React from 'react';
const Rank = ({results}) => {
const prediction = results.map((result) => {
const {ingredients} = result;
return (
<div>
<li className="celebrityName">{ingredients}</li>
</div>
);
});
if (prediction && prediction.length>1) {
return (
<div>
<div className='white f3'>
You look a lot like...
</div>
<div className='white f1'>
{results}
</div>
</div>
);
} else {
return (
<div>
</div>
)
}
};
export default Rank;

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 state not setting

it seems as if this set of code is not updating my state, and I am not sure why! The api is 100% sending back TRUE (as seen from axios console.log). Thank you advanced for the help!
import React, { Component } from 'react';
import axios from 'axios';
export class Test extends Component {
state = {
reponse: false
}
componentDidMount () {
axios.get(`/api`)
.then(res => {
console.log(res.data.success);
this.setState({ response: res.data.success });
});
}
render() {
if (this.state.reponse) {
return (
<div>
<h1>Response Gathered!</h1>
</div>
)
} else {
return (
<div>
<button onClick={() => console.log(this.state.reponse)}>Check State</button>
</div>
)
}
}
}
export default Test;
Change,
state = {
reponse: false
}
To,
state = {
response: false
}
There is a typo in state declaration (reponse to response)..
And modified code would look like,
class Test extends React.Component {
state = {
response: false
};
componentDidMount() {
axios.get(`/api`)
.then(res => {
console.log(res.data.success);
this.setState({ response: res.data.success });
});
}
render() {
if (this.state.response) {
return (
<div>
<h1>Response Gathered!</h1>
</div>
);
} else {
return (
<div>
<button onClick={() => console.log(this.state.response)}>
Check State
</button>
</div>
);
}
}
}
export default Test;
Working Codesandbox example
define state in the constructor function.
constructor(props) {
super(props);
this.state = {response: false};
}
btw, there was a spelling error.
render() {
return (
{this.state.response ?
<h1>Some text</h1> :
(<div>
<button onClick={() => console.log(this.state.response)}>
Check State
</button>
</div>)
}
);
}

Cant use Nextjs Queryparams props inside componentDidMount()

I am using Next.js and React and struck on this problem for days. So in Next.js pages i have dynamic page [postid].js, as follow,
import Layout from "../../components/layout";
import { useRouter } from "next/router";
import Singlepost from "../../components/post/singlepost";
export default function Post() {
const router = useRouter();
const postId = router.query.postid
return (
<Layout>
{console.log(router.query.postid)}
<h1>{router.query.postid}</h1>
<p>This is the post content.</p>
<Singlepost postId={postId}/>
</Layout>
);
}
Here I am sending query params as props(postId) to singlepost component.
But in the Singlepost component i am trying use this props inside componentDidMount which then makes api call to get data, but postId shows undefined so api call fails to fetch data.
componentDidMount() {
const postId = this.props.postId;
console.log("postId:", postId);
singlePost(postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
post: data,
likes: data.likes.length,
like: this.checkLike(data.likes),
comments: data.comments
});
}
});
}
So how do i get the prop value in componentDidMount? Or is there any other way should i approach this problem?
also here's my complete singlepost.js for Reference,
import React, { Component } from "react";
import {
singlePost,
remove,
like,
unlike
} from "../../components/post/apiPost";
import Link from "next/link";
import { isAuthenticated } from "../../components/auth";
import DefaultPost from "../../public/images/courses.png";
import Router, { withRouter } from "next/router";
class SinglePost extends Component {
constructor(props) {
super(props);
this.state = {
post: "",
redirectToHome: false,
redirectToBack: false,
redirectToSignin: false,
like: false,
likes: 0,
comments: [],
};
}
checkLike = likes => {
const userId = isAuthenticated() && isAuthenticated().user._id;
let match = likes.indexOf(userId) !== -1;
return match;
};
componentDidMount() {
const postId = this.props.postId;
console.log("postId:", postId);
singlePost(postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
post: data,
likes: data.likes.length,
like: this.checkLike(data.likes),
comments: data.comments
});
}
});
}
updateComments = comments => {
this.setState({ comments });
};
likeToggle = () => {
if (!isAuthenticated()) {
this.setState({ redirectToSignin: true });
return false;
}
let callApi = this.state.like ? unlike : like;
const userId = isAuthenticated().user._id;
const postId = this.state.post._id;
const token = isAuthenticated().token;
callApi(userId, token, postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
like: !this.state.like,
likes: data.likes.length
});
}
});
};
deletePost = () => {
const postId = this.props.quota;
const token = isAuthenticated().token;
remove(postId, token).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({ redirectToBack: true });
}
});
};
deleteConfirmed = () => {
let answer = window.confirm("Are you sure you want to delete your post?");
if (answer) {
this.deletePost();
}
};
renderPost = post => {
console.log(post);
const posterId = post.postedBy ? `/user/${post.postedBy._id}` : "";
const posterName = post.postedBy ? post.postedBy.name : " Unknown";
const { like, likes, comments } = this.state;
return (
<div className="column">
<img
src={`${process.env.REACT_APP_API_URL}/post/photo/${post._id}`}
alt={post.title}
onError={i => (i.target.src = `${DefaultPost}`)}
className="img-thunbnail"
style={{
height: "300px",
width: "100%",
objectFit: "cover"
}}
/>
<button onClick={this.likeToggle}>
<i className="far fa-thumbs-up text-success bg-dark" />
{likes} Like
</button>{" "}
<span className="button is-primary" onClick={() => Router.back()}>
<strong> Back to posts </strong>
</span>
{isAuthenticated().user &&
isAuthenticated().user._id === post.postedBy._id && (
<>
<span className="button is-warning">
<Link href={`/post/edit/${post._id}`}>
<strong> Update Post </strong>
</Link>
</span>
<button
onClick={this.deleteConfirmed}
className="button is-danger"
>
Delete Post
</button>
</>
)}
<div>
{isAuthenticated().user && isAuthenticated().user.role === "admin" && (
<div class="column">
<div className="columns">
<h5 className="column">Admin</h5>
<p>Edit/Delete as an Admin</p>
<span className="button is-warning">
<Link href={`/post/edit/${post._id}`}>
<a> Update Post </a>
</Link>
</span>
<button
onClick={this.deleteConfirmed}
className="button is-raised is-danger"
>
Delete Post
</button>
</div>
</div>
)}
</div>
<div>
<h4 className="raw"> Description: </h4>
<p className="column">{post.body}</p>
</div>
<br />
</div>
);
};
render() {
console.log("Render Quota:", this.props.postId, this.state.ispost);
const { postId } = this.props;
const {
post,
redirectToHome,
redirectToSignin,
redirectToBack
} = this.state;
if (redirectToHome) {
Router.push("/");
} else if (redirectToSignin) {
Router.push("/signin");
} else if (redirectToBack) {
Router.back();
}
return (
<section className="section">
<div className="container">
<h2 className="title">{post.title}</h2>
{!post ? (
<div className="hero">
<h2>Loading...</h2>
</div>
) : (
this.renderPost(post)
)}
</div>
</section>
);
}
}
export default SinglePost;
UPDATE- SOLVED
After Ayèch Hamza suggestion added getInitialProps method and thats exactly what needed.
So In [postid].js,
import React from "react";
import Layout from "../../components/layout";
import SinglePost from "../../components/post/singlePost";
Post.getInitialProps = async ctx => {
const PostId = ctx.query.postid;
return { PostId };
};
function Post({ PostId }) {
return (
<Layout>
<SinglePost PostId={PostId}/>
</Layout>
);
}
export default Post;
and in SinglePost.js,
componentDidMount() {
const postId = this.props.PostId;
singlePost(postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
post: data,
likes: data.likes.length,
like: this.checkLike(data.likes),
comments: data.comments
});
}
});
}

ReactJS | Loading State in component doesn't render Spinner

I am trying to make a React component that displays multiple renders based on props and state. So, while I wait for the promise to be resolved, I want to display a spinner Component
Main Renders:
NoResource Component => When the user is not valid
Spinner Component => When is loading on all renders
BasicRender Component => When data are fetched and is not loading
Below is my component:
/* eslint-disable react/prefer-stateless-function */
import React, { Component, Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { getUser, listUsers } from '../../config/service';
export class UserDetailsScreen extends Component {
static propTypes = {
match: PropTypes.shape({
isExact: PropTypes.bool,
params: PropTypes.object,
path: PropTypes.string,
url: PropTypes.string
}),
// eslint-disable-next-line react/forbid-prop-types
history: PropTypes.object,
label: PropTypes.string,
actualValue: PropTypes.string,
callBack: PropTypes.func
};
state = {
user: {},
error: '',
isloading: false
};
componentDidMount() {
this.fetchUser();
this.setState({ isLoading: true})
}
getUserUsername = () => {
const { match } = this.props;
const { params } = match;
return params.username;
};
fetchUser = () => {
getUser(this.getUserUsername())
.then(username => {
this.setState({
user: username.data,
isloading: false
});
})
.catch(({ message = 'Could not retrieve data from server.' }) => {
this.setState({
user: null,
error: message,
isLoading: false
});
});
};
validateUsername = () =>
listUsers().then(({ data }) => {
const { match } = this.props;
if (data.includes(match.params.username)) {
return true;
}
return false;
});
// eslint-disable-next-line no-restricted-globals
redirectToUsers = async () => {
const { history } = this.props;
await history.push('/management/users');
};
renderUserDetails() {
const { user, error } = this.state;
const { callBack, actualValue, label, match } = this.props;
return (
<div className="lenses-container-fluid container-fluid">
<div className="row">
.. More Content ..
{user && <HeaderMenuButton data-test="header-menu-button" />}
</div>
{user && this.validateUsername() ? (
<Fragment>
.. Content ..
</Fragment>
) : (
<div className="container-fluid">
{this.renderNoResourceComponent()}
</div>
)}
<ToolTip id="loggedIn" place="right">
{user.loggedIn ? <span>Online</span> : <span>Oflline</span>}
</ToolTip>
</div>
);
}
renderNoResourceComponent = () => {
const { match } = this.props;
return (
<div className="center-block">
<NoResource
icon="exclamation-triangle"
title="Ooops.."
primaryBtn="« Back to Users"
primaryCallback={this.redirectToUsers}
>
<h5>404: USER NOT FOUND</h5>
<p>
Sorry, but the User with username:
<strong>{match.params.username}</strong> does not exists
</p>
</NoResource>
</div>
);
};
renderSpinner = () => {
const { isLoading, error } = this.state;
if (isLoading && error === null) {
return <ContentSpinner />;
}
return null;
};
render() {
return (
<div className="container-fluid mt-2">
{this.renderSpinner()}
{this.renderUserDetails()}
</div>
);
}
}
export default withRouter(UserDetailsScreen);
The problem is:
I get the spinner along with the main component, and I am getting this error:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.. Can you please tell me what I am doing wrong.
The error is because you are running the renderUserDetailsComponent even when your API call is in loading state. You must only render the spinner on loading state
renderUserDetails() {
const { user, error, isLoading } = this.state;
if(isLoading) {
return null;
}
const { callBack, actualValue, label, match } = this.props;
return (
<div className="lenses-container-fluid container-fluid">
<div className="row">
.. More Content ..
{user && <HeaderMenuButton data-test="header-menu-button" />}
</div>
{user && this.validateUsername() ? (
<Fragment>
.. Content ..
</Fragment>
) : (
<div className="container-fluid">
{this.renderNoResourceComponent()}
</div>
)}
<ToolTip id="loggedIn" place="right">
{user.loggedIn ? <span>Online</span> : <span>Oflline</span>}
</ToolTip>
</div>
);
}

Categories