Get users except logged user - reactjs

I don't know how to form my question, but I'll try explain my problem. So I'm creating local meet app similar as (tinder). But I spent about 2 hours and can't figure out how to create a logic, that when I get all users from database I display all users at main page, but there is a problem, it also display the user which is already logged in and he can see himself at main page. Is there any solution to display all users except that user which is logged in? I was trying to use filter method, but can't figure it out how to create correct logic or maybe there's a simple method how to do it?
There I'm getting all users and display them:
export default function HomePage() {
const [allUsers, getAllUsers] = useState([])
useEffect(() => {
async function fetchUsers() {
const resp = await get('api')
getAllUsers(resp)
console.log(resp)
}
fetchUsers()
}, [])
return (
<div className='home__page'>
<Toolbar />
<div className="home__users">
{allUsers && allUsers.map((users, i) => <SingleUser users={users} key={i} />)}
</div>
<footer className='footer'>
<p>All rights reserved by Cartoon Match organization. To read more about our policy <Link to='/policy'>click here</Link>. </p>
</footer>
</div>
)
}
There's SingleUser component:
import React from "react";
export default function SingleUser({ users }) {
return (
<div className='single__user'>
<img src={users.image[0]} alt="" />
<h3>{users.firstName} {users.lastName}</h3>
<h4>{users.gender}</h4>
</div>
);
}
I'm also have a users secret saved in localstorage, maybe it could be easier to think simple solution

Normally you'll have access to the current loged in user stored in a global context, after user login. And there're two ways to filter the item you don't want to render in a list:
1. Conditional rendering (using && the Logical AND operator) inside the map() method:
export default function HomePage() {
const [currentUser, setCurrentUser] == useState(); // <- stores current loged in user
const [allUsers, getAllUsers] = useState([])
return (
<div className="home__users">
{allUsers && allUsers.map((user, i) => user.id !== currentUser.id && (<SingleUser users={user} key={user.id} />))}
</div>
)
}
2. Filter the list first and then render it, by chainning array method list.filter().map():
export default function HomePage() {
const [currentUser, setCurrentUser] == useState(); // <- stores current loged in user
const [allUsers, getAllUsers] = useState([])
return (
<div className="home__users">
{allUsers && allUsers.filter(user => user.id !== currentUser.id).map((user, i) => <SingleUser users={user} key={user.id} />)}
</div>
)
}
Array method accept an callback function, that we can spcify some condition, and check if every iteration of the item in the array meet such condition, if it meets the condition, then return the item, and filter() as a whole returns a shallow copy of the array itself, so we can continue to chain the method with map():
allUsers.filter(user => user.id !== currentUser.id).map(user => {...})
This means, if the user with id not equal to the loged in user, then return the user item.
See more at MDN docs: Logical AND (&&), filter method

Related

How could I achieve the state update effect in child components immediately?

This is just something I am playing with. Idea was to be able to import JSON data (users) from a remote location, display one user on each click of the button and also be able to delete a user with the delete button.
I have successfully imported JSON into my console and then have been able to show it onto the browser, been able to add users with a click of the button but, I am currently struggling with being able o delete users.
Along with the above, I also had a couple of side questions:
1)- Does creating multiple states affect negatively on the codes' performance in any ways?
2)- Could I have avoided creating count state? I tried with a local variable but that ends up loading from initial value each time the states change. I am sure there must be many ways. Recursion may be one of them which I have just thought of while typing my question and might see how to apply it.
3)- I ended up using id for val in my Blog component because I was failing to be able to access key attribute via event object in handleDel function. How could have I accessed key attribute and if there is any other better approach?
4)- In my count and counter states I tried different initial values but only the below values made the desired effect possible. Why is it so please?
Thanks in advance
import useFetch from './useFetch';
const Blog = ({counter, del}) => {
const {user} = useFetch();
return (
<div>
{
counter.map(val => (
<div key = {val} id={val}>
{user && <p>First name is : {user[val].firstName}</p>}
{user && <p>Last Name is : {user[val].lastName}</p>}
{user && <p>Age is : {user[val].age}</p>}
<button onClick = {del}>Delete</button>
</div>
))
}
</div>
)
}
export default Blog;
import Blog from './Blog';
import { useState } from "react";
const Home = () => {
const [count, setCount] = useState(1);
const [counter, setCounter] = useState([0])
const handleClick = () => {
setCount( count + 1 );
setCounter([...counter,...[count]]);
}
const handleDel = (e) => {
counter.splice(e.target.parentElement.id,1);
}
return (
<div>
hello from home
<button onClick = {handleClick}>Click for us to import a blog</button>
<Blog prp = {{count, counter}} del = {handleDel} />
</div>
)
}
export { Home };

Unable to loop single react component

I'm trying to loop my StudentInfo component so a blurb of information can be repeated down an array of 25 objects provided through an API for each object/a student (name, email, company, etc.) I'm not
sure where I'm going wrong; here is my attempted loop via the map function:
export default function StudentList() {
let students = useState(null);
return (
<div className="StudentList">
<div className="row">
<div className="col">
{students.map(function (student, index) {
if (index <= 25) {
return (
<div className="col" key={index}>
<StudentInfo data={student} />
</div>
);
}
})}
</div>
</div>
</div>
);
}
Can someone see what I'm missing or what I might have skipped? I usually assume that I must be doing something wrong because I'm still new at React and trying to adapt other code I used for a weather forecast app to be used for this, but I don't think it's translating over well.
When I run this, I see the first object twice, i.e. Name, Email, Company, etc. it shows the info for the same person twice, rather than the first person, the second person, etc. I want to be able to see this pull all the objects from the array.
Here is the information I'm pulling to return as an entry on student info:
export default function StudentInfo() {
const [info, addInfo] = useState(" ");
function setInfo(response) {
addInfo({
number: response.data.students[0].id,
first: response.data.students[0].firstName,
last: response.data.students[0].lastName,
email: response.data.students[0].email,
company: response.data.students[0].company,
skill: response.data.students[0].skill,
average: response.data.students[0].grades[0],
});
}
let url = "https://api.hatchways.io/assessment/students";
axios.get(url).then(setInfo);
return (
<div className="StudentInfo">
<h1>{info.number}.</h1>
<h2>
Name: {info.first} {info.last}
</h2>
<h2>Email: {info.email}</h2>
<h2>Company: {info.company}</h2>
<h2>Skill: {info.skill}</h2>
<h2>Average: {info.average}</h2>
</div>
);
}
I'm using "if (index <= 25)" as there are 25 total entries that I want showing, but as I mentioned, I have no doubt I'm going about this incorrectly. I want this to loop through all 25 objects with the above information, as I keep saying. I'm sorry if I'm not speaking technically enough to be understood, as I am still learning.
I just want this to return 25 times with info from each object so that it's all listed out.
This is what it currently looks like
UPDATE
I've been tinkering and was able to repeat the entry, however I'm now having trouble getting the unique information, i.e. I'm only seeing the first entry over and over. I'm not sure how to reiterate with new information since it's only going to response.data.students[0]. This is what my code looks like now, where the mapping is:
export default function StudentList() {
let [loaded, setLoaded] = useState(false);
let [students, setStudent] = useState(" ");
function doInfo(response) {
console.log(response.data);
setStudent(response.data.students[0].id);
setLoaded(true);
}
if (loaded) {
return (
<div className="StudentList">
<div className="row">
<div className="col">
{students.map(function (id) {
return <StudentInfo data={id} />;
})}
</div>
</div>
</div>
);
} else {
let url = "https://api.hatchways.io/assessment/students";
axios.get(url).then(doInfo);
}
}
Can someone help me code it so it runs through all 25 entries, not just the first one?
There are some errors in your code.
UseState
React useState hook returns an array with a value and a setter method to update the state useState docs.
const [students, setStudents] = useState(null);
Iterate over null values
If your state starts with a null value you will not be able to iterate over it. To avoid getting an error you should make sure to use the map operator when your state has a value.
{students && students.map(function (student, index) {
...
})}
Handling side effects
You should move your API request (and set your info state) inside of a useEffect (useEffect docs). This way you will set those values asynchronously after the component is mounted.
export default function StudentInfo() {
const [info, addInfo] = useState(null);
useEffect(() => {
async function fetchInfo() {
const url = "https://api.hatchways.io/assessment/students"; //should be a constant outside the component
const response = await axios.get(url);
addInfo({
number: response.data.students[0].id,
first: response.data.students[0].firstName,
last: response.data.students[0].lastName,
email: response.data.students[0].email,
company: response.data.students[0].company,
skill: response.data.students[0].skill,
average: response.data.students[0].grades[0],
});
fetchInfo();
}, [])
return (
<div className="StudentInfo">
{info &&
<h1>{info.number}.</h1>
<h2>
Name: {info.first} {info.last}
</h2>
<h2>Email: {info.email}</h2>
<h2>Company: {info.company}</h2>
<h2>Skill: {info.skill}</h2>
<h2>Average: {info.average}</h2>
}
</div>
);
}
export default function StudentList() {
const [students, setStudents] = useState([]);
Check React's documentation React.
Also check if it cannot be declared that way, you need to use a ternary if. Check that part in React's documenation Conditional rending
{students.map(function (student, index) {
index <= 25 && (
<div className="col" key={index}>
<StudentInfo data={student} />
</div>
);

Local storage being overwritten with new data

Hello guys I made a history page so the user can look at their past search history and so I used localstorage. On the history page, I am trying to render data that stays there and isn't changed when I go to search the api again. Instead I want it to keep adding data to the page. I was thinking the data would just keep being added to the new array in local storage but it overwrites the existing data with new data. Ive made an attempt to prevent this but I am stuck.
Here is my code of all of my pages
Search page
export default function SearchPage(props) {
// creating state to fetch the api
const [search, setSearch] = useState('')
// this is my function to monitor the words that are put into the input so these keywords that only matches specific data
// in the api and so one the data is fetched it renders the topic related to that keyword
const handleSearch = (event) => {
setSearch(event.target.value)
}
const handleSubmit = (event) => {
event.preventDefault();
// this is where I bring my useState variable to monitor the state of the key words in order to
// target specific data from the api
let url = `http://hn.algolia.com/api/v1/search_by_date?query=${search}`;
axios
.get(url)
.then((response) => {
const result = response.data.hits;
// this pushes the data fetched from the api to the results page using history
props.history?.push ({
pathname: '/results',
state: result,
});
})
// catching any errors to let me know if there is something going on in the .then function
.catch((error) => {
console.log(error)
console.log('Error while fetching data!')
})
}
return (
<div>
<div className="search-form-container">
{/* my form in where I search data by keywords */}
<form onSubmit={handleSubmit}>
<input type='text' placeholder="search" onChange={handleSearch} value={search}/>
<button type="submit">Search</button>
</form>
<hr/>
<Link to="/history">Your Search History</Link>
</div>
</div>
)
}
Results page
export default function ResultsPage(props) {
console.log(props)
// the variable declared below is where I am bringing in the data through history from the api.
const data = props.history.location.state;
let storedData = localStorage.setItem('data', JSON.stringify(data))
console.log(storedData)
// being that I am using history, I can map through the array of objects on the results page as shown below and then render it
const hitList = data.map((data, idx) => {
return (
<ul key={idx}>
<div>
<li> Author: {data.author}</li>
<li>Story Title: {data.story_title}</li>
<li>Comment: {data.comment_text}</li>
<li>Created on: {data.created_at}</li>
<li></li>
</div>
<hr/>
</ul>
)
})
return (
<div>
<Link to='/'><h1>Search</h1></Link>
<Link to='/history'><h1>Your History</h1></Link>
{/* In order for me to show my data I first had to map through the array of objects and then put the variable "hitlist" in the return */}
<h3>{hitList}</h3>
</div>
)
}
History page
export default function SearchHistoryPage(item) {
const storedData = JSON.parse(localStorage.getItem('data'));
storedData.push(item);
localStorage.setItem('data', JSON.stringify(storedData));
console.log(storedData);
const searchHistory = storedData.map((data, idx) => {
return (
<ul key={idx}>
<li> Author: {data.author}</li>
<li>Story Title: {data.story_title}</li>
<li>Comment: {data.comment_text}</li>
<li>Created on: {data.created_at}</li>
</ul>
)
})
return (
<div>
<h2>{searchHistory}</h2>
</div>
)
}
In your Results page, you are overwriting your localStorage 'data' key with only the results fetched from Search page.
What you can do is to fetch your "history" (localStorage 'data') before you push the new results in your Results page, and not in your History page as so:
In Results page:
const data = props.history.location.state;
// ADD THESE 2 LINEs
const historicData = JSON.parse(localStorage.getItem('data'));
historicData.push(data)
// Store "historicData" instead of "data"
// let storedData = localStorage.setItem('data', JSON.stringify(data))
let storedData = localStorage.setItem('data', JSON.stringify(historicData))
console.log(storedData)
In History page:
// Just use localStorage 'data'.
// Do not modify anything in History page since you are not taking any actions here.
const storedData = JSON.parse(localStorage.getItem('data'));
storedData.push(item);
// localStorage.setItem('data', JSON.stringify(storedData)); <-- REMOVE
// console.log(storedData); <-- REMOVE

Single responsibility in React component

I was learning Single responsibility principle in React and created such component:
import React from "react";
import {useGetRemoteData} from "./useGetRemoteData";
export const SingleResponsibilityPrinciple = () => {
const {filteredUsers , isLoading} = useGetRemoteData()
const showDetails = (userId) => {
const user = filteredUsers.find(user => user.id===userId);
alert(user.contact)
}
return <>
<div> Users List</div>
<div> Loading state: {isLoading? 'Loading': 'Success'}</div>
{filteredUsers.map(user => {
return <div key={user.id} onClick={() => showDetails(user.id)}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
</>
}
As you can see above, I have this code
const user = filteredUsers.find(user => user.id===userId);
The question is Is it best practice that if whenever we use map, reduce or any array function in React component, we should extract that logic out of a component, that is, filteredUsers.find(user => user.id===userId); should be extracted and we need to create utility function. So, function should not care about how a particular thing is done. Is it true?
Thank you for your question. I want to advice as follows
It is better for your to check if filteredUsers exists or not in your return.
{filteredUsers?.map(user=>{
//your code
})
You don't need to get specific user as you already loop in your map method. Just simply do something like this
{filteredUsers.map(user => {
return <div key={user.id} onClick={() => showDetails(alert(user.contact))}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
Remember the difference between find, filter method of Javascript array. If you have unique value according to userId, you simply use find method to get the first value not array, if you use filter, you get arrays of the condition. Are you sure you don't need to alert(user[0].contact), not alert(user.contact)?

How to code a good re-render parent call?

render my parent component from a call in its child component. But I don't know how to write it correctly.
The context is: When the page is loaded, the code search the userId in localStorage to download the data of a user to display its information.
A link exists in child component to removed the idUser and got an empty form. The problem is that my parent don't re-render automatically.
<Link to="/consultation" onClick={() => {localStorage.removeItem('idUser'); this.props.callBack();}}>here.</Link>
-
else if(user){contents = [<Message_UserIdentified user callBack={this.forceUpdate()}/>, contentform];}
I tried something, but it's not work. Could you help me please ?
I don't know how to parse it (the item "user" and in same time the callBack).
The code write me that it don't find the function forceUpdate().
This is my parents page called "Consultation" (I removed some parts of code to be clearer):
import { Message_EmptyUserID, Message_NeedToBeConnected, Message_UserIdentified } from '../component/message_consultation';
const Consultation = () => {
const [user, setUser] = useState(null);
const [login] = useState(jwtUtils.checkToken());
const [userId, setUserId] = useState(false);
function handleChange(event) {
setUser({
...user,
[event.target.name]: event.target.value
})
};
useEffect(() => {
if (login === false || localStorage.getItem("idUser") === null) return;
axios.get(`http://localhost:5000/users/${localStorage.getItem("idUser")}`, {
headers: {
'token': localStorage.getItem('token')
}
})
.then(res => {
setUser(res.data);
if(res.data !== undefined){
setUserId(true);
}
})
.catch(error => console.log(error))
}, []);
let contents, contentform;
contentform = (
<div >...
</div>
);
if (login === false) contents = Message_NeedToBeConnected();
else if (userId === false) contents = contentform;
else if(user){contents = [<Message_UserIdentified user callBack={this.forceUpdate()}/>, contentform];}
return (
<div className="case card border-secondary mb-3" styles="max-width: 20rem;">
<div className="card-header">Consultation</div>
<div className="card-body">
{contents}
</div>
</div>
);
}
export default Consultation;
This is my child component called "Message_UserIndentified":
const Message_UserIdentified = (user) => {
return(
<Alert color="primary" className="alert alert-dismissible alert-info">
<h4>{user === null || user === undefined ? "" : user.firstname} {user === null || user === undefined ? "" : user.lastname}</h4>
If you are not {user === null || user === undefined ? "" : user.firstname} and you are <mark>already registered</mark>, find your consultation <Link to="/register" onClick={localStorage.removeItem('idUser')}>here.</Link> <hr/>
If you are not {user === null || user === undefined ? "" : user.firstname} and your are <mark>not registered</mark>, add your consultation <Link to="/consultation" onClick={() => {localStorage.removeItem('idUser'); this.props.callBack();}}>here.</Link>
</Alert>
);
}
First of all, you mix up the hooks API with the class API. You need to choose one, React doesn't allow to use both in a single component.
React functional components don't have this and this.forceUpdate. See the official note on how to force update in a functional component with hooks:
const Consultation = () => {
const [ignored, forceUpdate] = React.useReducer(x => x + 1, 0);
// ...
return <Message_UserIdentified user callBack={forceUpdate} />;
};
Second, you mustn't call the function when you want to pass it as a callback. When you run callBack="forceUpdate()", the function is executed once and the returned value is passed to the child prop. You need to run callBack="forceUpdate" to pass the function itself so that the child component can execute it whenever it wants.
This rule is applicable to class components too, but you should also bind the forceUpdate method to the current component:
<button onClick={() => this.forceUpdate()}>Demo</button>
// or
<button onClick={this.forceUpdate.bind(this)}>Demo</button>
From the architecture point of view, you shouldn't use localStorage to share data between components of the application because localStorage isn't reactive (it doesn't notify when a content changes) so you'll struggle updating all the components manually. You'd rather read'n'write localStorage only in Consultation or use a reactive state storage like Redux and MobX (in more complex applications). Local storage may be used at the same time: read on an application start and write when something changes.

Resources