how i can change this code to functional component in react - reactjs

Here how i can change this code to functional component. If i try to change this to functional component in console data is keep on printing. how i can solve that error. Kindly provide solution for this.
import React, { Component } from "react";
import { connect } from "react-redux";
import { getUsers } from "../actions/exampleAction";
function Users (props) {
props.getUsers();
const { users } = props.users;
console.log(props.users);
return (
<div>
{users.length &&
users.map((user) => {
return (
<React.Fragment key={user.id}>
<h6> {user.first_name} </h6>
<p> {user.last_name} </p>
<p> {user.email}</p>
<p>
<img key={user.avatar} src={user.avatar} alt="avatar" />
</p>
</React.Fragment>
);
})}
</div>
);
}
const mapStateToProps = (state) => ({ users: state.users });
export default connect(mapStateToProps, { getUsers })(Users);

Since, at the during the initial rendering of the component, props.getUsers() is called, when it is changed, again the props changes and component is rendered.
That's why you get multiple console logs.
You can read about side effects and using hooks on Functional Components here.
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { getUsers } from "../actions/exampleAction";
function Users(props) {
const { users } = props.users;
useEffect(() => {
props.getUsers();
}, []);
return (
<div>
{users.length &&
users.map((user) => {
return (
<React.Fragment key={user.id}>
<h6> {user.first_name} </h6>
<p> {user.last_name} </p>
<p> {user.email}</p>
<p>
<img key={user.avatar} src={user.avatar} alt="avatar" />
</p>
</React.Fragment>
);
})}
</div>
);
}
const mapStateToProps = (state) => ({ users: state.users });
export default connect(mapStateToProps, { getUsers })(Users);

You can also use hook available from "react-redux" instead of mapStateToProps or mapDispatchToProps as given below
And you don't need to pass getUsers as props because you already import it at the top so you can directly use it without props.
So go through following code and improve your coding standard
import React, { useEffect } from "react";
import {useSelector} from 'react-redux'
import { getUsers } from "../actions/exampleAction";
function Users(props) {
const users = useSelector(state => state.users);
useEffect(() => {
getUsers();
}, []);
if(!users.length) return null;
const userData = users.map((user) => {
return (
<React.Fragment key={user.id}>
<h6> {user.first_name} </h6>
<p> {user.last_name} </p>
<p> {user.email}</p>
<p>
<img key={user.avatar} src={user.avatar} alt="avatar" />
</p>
</React.Fragment>
);
});
return (
<div>
{userData}
</div>
);
}
export default Users;

Related

in MERN, Response given and using useState to update new state with new fetched data, but not visually visible in my website even though logic works

By using console.log(responseData.places) I have checked the fetching works since I am using a hook for this and seems to work fine until I setLoadedPlaces with is the method I use to update the loadedPlaces which I later use to get the values to fill the frontend part of the website.
This is the output I get from this console.log I did and the values are correct.
[{…}]
0: address: "sis se puede
busrespect: 'tu puedes',
creator: "6384e2f543f63be1c560effa"
description: "al mundial"
id: "6384e30243f63be1c560f000"
image:"https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Empire_State_Building_%28aerial_view%29.jpg/400px-Empire_State_Building_%28aerial_view%29.jpg"location: {lat: -12.086158, lng: -76.898019}
title: "Peru"
__v: 0
_id: "6384e30243f63be1c560f000"[[Prototype]]:
Objectlength: 1[[Prototype]]: Array(0)
So after this this the code I have in the frontend (SINCE the backend works properly) Let me know if you have any doubts with this logic
This is UserPlaces.js
import React, {useState, useEffect } from 'react';
import PlaceList from '../components/PlaceList';
import { useParams } from 'react-router-dom';
import { useHttpClient } from '../../shared/hooks/http-hook';
import ErrorModal from '../../shared/components/UIElements/ErrorModal';
import LoadingSpinner from '../../shared/components/UIElements/LoadingSpinner';
const UserPlaces = () => {
const {loadedPlaces, setLoadedPlaces} = useState();
const {isLoading, error, sendRequest, clearError } = useHttpClient();
const userId = useParams().userId;
useEffect(() => {
const fetchPlaces = async () => {
try {
const responseData = await sendRequest(
`http://localhost:5000/api/places/user/${userId}`
);
console.log(responseData.bus_stops)
setLoadedPlaces(responseData.bus_stops);
} catch (err) {}
};
fetchPlaces();
}, [sendRequest, userId]);
return (
<React.Fragment>
<ErrorModal error={error} onClear={clearError} />
{isLoading && (
<div className="center">
<LoadingSpinner />
</div>
)}
{!isLoading && loadedPlaces && <PlaceList items={loadedPlaces} />}
</React.Fragment>
);
};
export default UserPlaces;
This is Place-List.js
import React from 'react';
import "./PlaceList.css"
import Card from '../../shared/components/UIElements/Card'
import PlaceItem from './PlaceItem';
import Button from '../../shared/components/FormElements/Button';
const PlaceList = props => {
if (props.items.length === 0) {
return (
<div className='place-list-center'>
<Card>
<h2>No bus stops available. Be the first one to create one!</h2>
<Button to='/places/new'> Create Bus Stop </Button>
</Card>
</div>
);
}
return (
<ul className="place-list">
{props.items.map(bus_stops => (
<PlaceItem
key={bus_stops.id}
id={bus_stops.id}
image={bus_stops.image}
title={bus_stops.title}
busrespect={bus_stops.busrespect}
description={bus_stops.description}
address={bus_stops.address}
creatorId={bus_stops.creator}
coordinates={bus_stops.location}
/>
))}
</ul>
);
};
export default PlaceList;
This is PlaceItem.js
import React, { useState } from 'react';
import { useContext } from 'react';
import Card from '../../shared/components/UIElements/Card';
import Button from '../../shared/components/FormElements/Button';
import Modal from '../../shared/components/UIElements/Modal';
import Map from '../../shared/components/UIElements/Map';
import {AuthContext} from '../../shared//context/auth-context'
import "./PlaceItem.css";
const PlaceItem = props => {
const auth = useContext(AuthContext);
const [showMap, setShowMap] = useState(false);
const [showConfirmModal, setShowConfirmModal] = useState(false);
const openMapHandler = () => setShowMap(true);
const closeMapHandler = () => setShowMap(false);
const showDeleteWarningHandler = () => {
setShowConfirmModal(true);
};
const cancelDeleteHandler = () => {
setShowConfirmModal(false);
};
const confirmDeleteHandler = () => {
setShowConfirmModal(false); //when clicked close the new Modal
console.log('DELETING...');
};
return (
<React.Fragment>
<Modal show={showMap}
onCancel={closeMapHandler}
header={props.address}
contentClass="place-item__modal-content"
footerClass="place-item__modal-actions"
footer={<Button onClick={closeMapHandler}>Close </Button>}
>
<div className='map-container'>
<Map center={props.coordinates} zoom={16}/> {/* Should be props.coordinates but we writing default data for now until geocoding solved. */}
</div>
</Modal>
<Modal
show={showConfirmModal}
onCancel={cancelDeleteHandler}
header="Are you entirely sure?"
footerClass="place-item__modal-actions"
footer={
<React.Fragment>
<Button inverse onClick={cancelDeleteHandler}>
CANCEL
</Button>
<Button danger onClick={confirmDeleteHandler}>
DELETE
</Button>
</React.Fragment>
}
>
<p>
Do you want to proceed and delete this place? Please note that it
can't be undone thereafter.
</p>
</Modal>
<li className='"place=item'>
<Card className="place-item__content">
<div className='place-item__image'>
<img src={props.image} alt={props.title}/>
</div>
<div className='place-item__info'>
<h2>{props.title}</h2>
<h3>{props.address}</h3>
<p>{props.description}</p>
<p>{props.busrespect}</p>
</div>
<div className='place-item__actions'>
<Button inverse onClick={openMapHandler}> VIEW ON MAP</Button>
{auth.isLoggedIn && (<Button to={`/places/${props.id}`}> EDIT</Button> )}
{auth.isLoggedIn &&<Button danger onClick={showDeleteWarningHandler}> DELETE </Button>}
</div>
</Card>
</li>
</React.Fragment>
);
};
export default PlaceItem;
This is auth-context:
import { createContext } from "react";
export const AuthContext = createContext({
isLoggedIn: false,
userId: null,
login: () => {},
logout: () => {}});
This is is Modal.js
import React from 'react';
import ReactDOM from 'react-dom';
import Backdrop from './Backdrop';
import { CSSTransition } from 'react-transition-group';
import './Modal.css';
const ModalOverlay = props => {
const content =(
<div className={`modal ${props.className}`} style = {props.style}>
<header className={`modal__header ${props.headerClass}`}>
<h2>{props.header}</h2>
</header>
<form
onSubmit={
props.onSubmit ? props.onSubmit : event => event.preventDefault()
}
>
<div className={`modal__content ${props.contentClass}`}>
{props.children}
</div>
<footer className={`modal__content ${props.footerClass}`}>
{props.footer}
</footer>
</form>
</div>
);
return ReactDOM.createPortal(content, document.getElementById('modal-hook'));
};
const Modal = props => {
return (
<React.Fragment>
{props.show && <Backdrop onClick={props.onCancel} />}
<CSSTransition in={props.show}
mountOnEnter
unmountOnExit
timeout={200}
classNames="modal"
>
<ModalOverlay {...props}/>
</CSSTransition>
</React.Fragment>
);
};
export default Modal;
Also Trust the routing is correct since I have checked it already and I am just wondering if the logic in REACT with loadedPlaces, PlaceItema and PlaceList makes sense and it working. Let me know please. It will be really helpful.
Summary: Not getting any error but no visual data appears in the scren just the header of my website and the background (rest is empty) even though logic is functional.
const {loadedPlaces, setLoadedPlaces} = useState();
change the above line to
const [loadedPlaces, setLoadedPlaces] = useState();

How to set ratings individually with localstorage?

Okay so as you can tell,I am building a recipe app where in each recipe detail page, I can rate the the recipe...but now the problem is that when I rate one recipe now, the same rating is set for all the other recipes as well. Any idea how to fix that? Apreciate any advice...thanks so much :))
Details.js
import { useParams } from 'react-router-dom';
import './Details.css';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Image from './vitaminDfood-1132105308-770x553.jpg';
import {Link} from 'react-router-dom'
import ReactStars from 'react-rating-stars-component'
import { RecipeContext } from './RecipeContext';
import { useContext } from 'react';
function Details() {
const [details, setDetails] = useState([]);
const { recipeId } = useParams();
const{recipes,setRecipes}=useContext(RecipeContext)
const initialRatings = JSON.parse(localStorage.getItem("rating") || "[]");
const[rating,setRating]=useState(initialRatings)
useEffect(() => {
axios
.get(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}`)
.then((res) => setDetails(res.data));
});
useEffect(() => {
localStorage.setItem("rating", JSON.stringify(rating));
}, [rating])
const ratingChanged = (newRating) => {
var rate={
score:newRating
}
setRating(newRating)
axios.post(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}/ratings`,rate)
.then((res) => {
console.log(res.data)
setRecipes(recipes)
})
};
return (
<>
<div className="details">
<div className="food-photo">
<img src={Image} alt="" />
<Link to="/"> <i className="fas fa-arrow-left arrow"></i></Link>
<h1>{details.name}</h1>
</div>
<div className="star-line">
{new Array(rating).fill(null).map(() => (
<i className="fas fa-star stari"/>
))}
<p className="duration"><i className="far fa-clock"></i>{details.duration}<span>min.</span></p>
</div>
<p className="info">{details.info}</p>
<h1 className="ingredience">Ingredience</h1>
<div className="ing">{details.ingredients}</div>
<h1 className="ingredience">Příprava</h1>
<p className="description">{details.description}</p>
</div>
<div className="stars">
<ReactStars
classNames="star"
size={48}
onChange={ratingChanged}
count={5}
value={1}
edit
/>
</div>
</>
);
}
export default Details;
You have a couple options:
Store a different localStorage key for each recipe
Just have one large "ratings" key in localStorage and make that an object keyed off of your recipeId.
I think the first option is the most straightforward and can be accomplished with these minor modifications:
const initialRatings = JSON.parse(localStorage.getItem(`ratings-${recipeId}`) || "[]");
useEffect(() => {
localStorage.setItem(`ratings-${recipeId}`, JSON.stringify(rating));
}, [rating, recipeId])

How to save the rating to localstorage so it doesn't dissapear after refreshing?

How can I let the star rating stay even after I leave the details page or after refreshing? Setting of the state isnt enough here. I suppose Localstorage type of saving should be used here right? Not really sure how to do that here. Hope you can help. Apreciate any advice guys. Thanks so much :))).
Details.js
import { useParams } from 'react-router-dom';
import './Details.css';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Image from './vitaminDfood-1132105308-770x553.jpg';
import {Link} from 'react-router-dom'
import ReactStars from 'react-rating-stars-component'
import { RecipeContext } from './RecipeContext';
import { useContext } from 'react';
function Details() {
const [details, setDetails] = useState([]);
const { recipeId } = useParams();
const[rating,setRating]=useState([])
const{recipes,setRecipes}=useContext(RecipeContext)
useEffect(() => {
axios
.get(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}`)
.then((res) => setDetails(res.data));
});
const ratingChanged = (newRating) => {
var rate={
score:newRating
}
setRating(newRating)
axios.post(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}/ratings`,rate)
.then((res) => {
console.log(res.data)
setRecipes(recipes)
})
};
return (
<>
<div className="details">
<div className="food-photo">
<img src={Image} alt="" />
<Link to="/"> <i className="fas fa-arrow-left arrow"></i></Link>
<h1>{details.name}</h1>
</div>
<div className="star-line">
{new Array(rating).fill(null).map(() => (
<i className="fas fa-star stari"/>
))}
<p className="duration"><i className="far fa-clock"></i>{details.duration}<span>min.</span></p>
</div>
<p className="info">{details.info}</p>
<h1 className="ingredience">Ingredience</h1>
<div className="ing">{details.ingredients}</div>
<h1 className="ingredience">Příprava</h1>
<p className="description">{details.description}</p>
</div>
<div className="stars">
<ReactStars
classNames="star"
size={48}
onChange={ratingChanged}
count={5}
value={1}
edit
/>
</div>
</>
);
}
export default Details;
You can use an effect with the useEffect hook to save your ratings to localStorage. On page load, you can load initial ratings from localStorage as well and use that as the default value in useState.
const initialRatings = JSON.parse(localStorage.getItem("ratings") || "[]");
function Details() {
const [ratings, setRatings] = useState(initialRatings);
// Save to localstorage on change
useEffect(() => {
localStorage.setItem("ratings", JSON.stringify(ratings));
}, [ratings])
}

React - simple ul li filter

I am new to React and I am trying to filter a list of emails in .users-list. I just want to return what the user is typing on the SearchBox but it does not work. Any suggestions?
Dashboard.js
import React, { Component } from "react";
import Button from "../Button/Button";
import SearchBox from "../SearchBox/SearchBox";
import "./Dashboard.css";
import fire from "../../fire"
class Dashboard extends Component {
constructor(){
super();
this.state = {
users:[],
searchField:''
}
}
handleLogout = () => {
fire.auth().signOut();
};
render() {
const {users, searchField} = this.state
const filteredUsers = users.filter(users => (users.users.toLowerCase.inc))
return (
<div>
<h2>Welcome</h2>
<button onClick={this.handleLogout}>Logout</button>
<div className="users-container">
<div>
<SearchBox
placeholder="Enter email..."
handleChange={(e) =>
this.setState({ searchField: e.target.value})
}
/>
</div>
<ul className="users-list">
<li>
<span>jean#gmail.com</span>
</li>
<li>
<span>albert#gmail.com</span>
</li>
<li>
<span>kevin#gmail.com</span>
</li>
<li>
<span>lucie#gmail.com</span>
</li>
</ul>
</div>
</div>
);
}
}
export default Dashboard;
SearchBox.js
import React from 'react';
const SearchBox = (props) => {
return(
<input
type='search'
className='search'
placeholder={props.placeholder}
onChange={props.handleChange}
/>
)
}
export default SearchBox
You can follow: codesandbox DEMO
For optimize performance:
useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
import React, { useMemo, useState } from "react";
import "./styles.css";
const usersData = [
{id:1,email: 'jean#gmail.com'},
{id:2,email: 'albert#gmail.com'},
{id:3,email: 'kevin#gmail.com'},
]
export default function App() {
const [search, setSearch] = useState("");
const filteredUsers = useMemo(() => {
if (search) {
return usersData.filter(
(item) =>
item.email
.toLowerCase()
.indexOf(search.toLocaleLowerCase()) > -1
);
}
return usersData;
}, [search]);
return (
<div className="App">
<h1>users list</h1>
<input type="search" name="search" value={search} onChange={e => setSearch(e.target.value)} />
<ul>
{filteredUsers.length > 0 ?
(filteredUsers && filteredUsers.map(item => (
<li key={item.id}>{item.email}</li>
))): <div>empty</div>
}
</ul>
</div>
);
}

how to toggle comments component via onClick function ,react js

import React, { Fragment, useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { getPosts } from '../redux/actions/#posts';
import PostItem from '../components/posts/PostItem';
import CommentForm from '../components/posts/CommentForm';
import Comment from '../components/posts/Comment';
import '../styles/posts/postComponent.scss';
const Posts = ({
getPosts,
posts: { posts, isLoading },
isAuthenticated,
user
}) => {
useEffect(() => {
getPosts();
}, []);
const [show, toggleShow] = useState(false);
console.log(show);
const postsList = isLoading ? (
<div>posts are loading</div>
) : (
posts.map(post => {
return (
<div className='post'>
<PostItem
key={post._id}
auth={isAuthenticated}
user={user}
id={post._id}
title={post.title}
body={post.text}
author={post.name}
avatar={post.avatar}
date={post.date}
likes={post.likes}
comments={post.comments.map(comment => comment)}
toggleShow={toggleShow}
show={show}
/>
<CommentForm id={post._id} />
{post.comments.map(
comment =>
show && (
<Comment
key={comment._id}
comment={comment}
auth={isAuthenticated}
admin={user}
show={show}
/>
)
)}
</div>
);
})
);
return (
<Fragment>
<Link to='/add-post'>add Post</Link>
<div>{postsList}</div>
</Fragment>
);
};
Posts.propTypes = {
getPosts: PropTypes.func.isRequired,
posts: PropTypes.object.isRequired,
isAuthenticated: PropTypes.bool.isRequired
};
const mapStateToProps = state => {
// console.log(state.posts.posts.map(post => post.likes));
// console.log(state);
return {
posts: state.posts,
isAuthenticated: state.auth.isAuthenticated,
user: state.auth.user
};
};
export default connect(mapStateToProps, { getPosts })(Posts);
import React, { Fragment } from 'react';
import '../../styles/posts/postComponent.scss';
const Comment = ({
comment: { user, avatar, name, date, text },
admin,
auth
}) => {
return (
<Fragment>
<div className='c-container'>
<div className='c-img-text'>
<img className='c-img' height={'40px'} src={avatar} />
<div className='c-nt'>
<a href='#' className='c-n'>
{name}
</a>
<span className='c-t'> {text}</span>
<i className='c-d'>{date}</i>
</div>
{auth && admin
? admin._id === user && <div className='c-toggle'>...</div>
: ''}
</div>
</div>
</Fragment>
);
};
export default Comment;
I have a list of posts stored in redux, and mapped through it to create components. Now each component has a some body and comments.
I want to show the comments only after onClick event .
Below is the code I have come up with , and on Click it is toggling all the comments of all the Components.How can I toggle comments of an individual Component.

Resources