Rendering User api data, through constant variable in React Native - reactjs

I am trying to render my Users api data, through variable contact in ProfileComponent, but variable contact isn't assigning to the item data. When I console.log(data), it responds with all the information, such as profile_picutre, banner_picture, first_name, ect, which is what I need. I know I'm close but I don't understand why the contact varible isn't grabbing the data item.
I think my problem would be solved if I can get item together with newContact, but I don't know. Also sorry if my question is bare, I'm still trying to learn all the react native terminologies. If someone could help point me in the right direction, I would greatly appreciate that.
PS: I'm using axios to fetch my api, and using useContext to store my user, with my provider.
const ProfileScreen = () => {
const {params: {item = {}} = {}} = useRoute();
const {
contactsDispatch,
contactsState: {
getProfile: {data, loading, error},
},
} = useContext(GlobalContext);
useEffect(() => {
getProfile()(contactsDispatch);
}, []);
const contactsRef = useRef([]);
const prev = contactsRef.current;
contactsRef.current = data;
const newList = contactsRef.current;
const newContact = newList.find(
(item) => !prev.map((i) => i.id).includes(item.id),
);
console.log(data)
return (
<ProfileComponent
data={data}
loading={loading}
contact={data}
/>
);
};
export default ProfileScreen;
const ProfileComponent = ({
contact,
...,
...,
}) => {
const {
profile_picture,
banner_picture,
first_name,
phone_number,
last_name,
bio,
username,
file,
caption,
} = contact;
return(
...
)
)}

contactsRef.current must be a NodeElement.
Considering data is a NodeElement:
Objects coming from context start as undefined, so you need to check them first:
useEffect(() => {
if(getProfile != undefined && contactsDispatch != undefined) {
getProfile()(contactsDispatch);
}
}, [contactsDispatch, getProfile]);

Related

How can I make syntax less repetitive (DRY)?

The object of this app is to allow input text and URLs to be saved to localStorage. It is working properly, however, there is a lot of repeat code.
For example, localStoredValues and URLStoredVAlues both getItem from localStorage. localStoredValues gets previous input values from localStorage whereas URLStoredVAlues gets previous URLs from localStorage.
updateLocalArray and updateURLArray use spread operator to iterate of previous values and store new values.
I would like to make the code more "DRY" and wanted suggestions.
/*global chrome*/
import {useState} from 'react';
import List from './components/List'
import { SaveBtn, DeleteBtn, DisplayBtn, TabBtn} from "./components/Buttons"
function App() {
const [myLeads, setMyLeads] = useState([]);
const [leadValue, setLeadValue] = useState({
inputVal: "",
});
//these items are used for the state of localStorage
const [display, setDisplay] = useState(false);
const localStoredValues = JSON.parse(
localStorage.getItem("localValue") || "[]"
)
let updateLocalArray = [...localStoredValues, leadValue.inputVal]
//this item is used for the state of localStorage for URLS
const URLStoredVAlues = JSON.parse(localStorage.getItem("URLValue") || "[]")
const tabBtn = () => {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
const url = tabs[0].url;
setMyLeads((prev) => [...prev, url]);
// update state of localStorage
let updateURLArray = [...URLStoredVAlues, url];
localStorage.setItem("URLValue", JSON.stringify(updateURLArray));
});
setDisplay(false)
};
//handles change of input value
const handleChange = (event) => {
const { name, value } = event.target;
setLeadValue((prev) => {
return {
...prev,
[name]: value,
};
});
};
const saveBtn = () => {
setMyLeads((prev) => [...prev, leadValue.inputVal]);
setDisplay(false);
// update state of localStorage
localStorage.setItem("localValue", JSON.stringify(updateLocalArray))
};
const displayBtn = () => {
setDisplay(true);
};
const deleteBtn = () => {
window.localStorage.clear();
setMyLeads([]);
};
const listItem = myLeads.map((led) => {
return <List key={led} val={led} />;
});
//interates through localStorage items returns each as undordered list item
const displayLocalItems = localStoredValues.map((item) => {
return <List key={item} val={item} />;
});
const displayTabUrls = URLStoredVAlues.map((url) => {
return <List key={url} val={url} />;
});
return (
<main>
<input
name="inputVal"
value={leadValue.inputVal}
type="text"
onChange={handleChange}
required
/>
<SaveBtn saveBtn={saveBtn} />
<TabBtn tabBtn={tabBtn} />
<DisplayBtn displayBtn={displayBtn} />
<DeleteBtn deleteBtn={deleteBtn} />
<ul>{listItem}</ul>
{/* displays === true show localstorage items in unordered list
else hide localstorage items */}
{display && (
<ul>
{displayLocalItems}
{displayTabUrls}
</ul>
)}
</main>
);
}
export default App
Those keys could be declared as const and reused, instead of passing strings around:
const LOCAL_VALUE = "localValue";
const URL_VALUE = "URLValue";
You could create a utility function that retrieves from local storage, returns the default array if missing, and parses the JSON:
function getLocalValue(key) {
return JSON.parse(localStorage.getItem(key) || "[]")
};
And then would use it instead of repeating the logic when retrieving "localValue" and "URLValue":
const localStoredValues = getLocalValue(LOCAL_VALUE)
//this item is used for the state of localStorage for URLS
const URLStoredVAlues = getLocalValue(URL_VALUE)
Similarly, with the setter logic:
function setLocalValue(key, value) {
localStorage.setItem(key, JSON.stringify(value))
}
and then use it:
// update state of localStorage
let updateURLArray = [...URLStoredVAlues, url];
setLocalValue(URL_VALUE, updateURLArray);
// update state of localStorage
setLocalValue(LOCAL_VALUE, updateLocalArray)

Problem accessing data of an array created from the state in Reactjs

I have an array of country codes and I need to have the name.
I am trying to access the countries data from the state (axios call) and from there filter by country code, and from that new array, extract the common name of the country.
(I am using the restcountries.com api).
-If I create a new state to map from, I get the too many re-renders.
-Right now, Although the border countries info is there, I can't access it, I get the "Cannot read properties of undefined" error, that usually is tied to a lifecycle issue, therefore I am using a condition on when to access the information.
Still I am not able to get it stable and return the name that I need.
Can someone please take a look and tell me what am I doing wrong?
Thanks in advance
import axios from "axios";
const BorderCountries = (props) => {
const [countriesList, setCountriesList] = useState([]);
useEffect(() => {
axios
.get(`https://restcountries.com/v3.1/all`)
.then((countries) => setCountriesList(countries.data))
.catch((error) => console.log(`${error}`));
}, []);
const getCountryName = () => {
const codes = props.data;
const borderCountries = [];
codes.map((code) => {
const borderCountry = countriesList.filter((country) =>
country.cca3.includes(code)
);
borderCountries.push(borderCountry);
});
// console.log(borderCountries);
if (props.data.length === borderCountries.length) {
const borderName = borderCountries.map((border) =>
console.log(border[0].name.common)
);
return borderName
}
};
return (
<div>
<h3>Border Countries:</h3>
{getCountryName()}
</div>
);
};
export default BorderCountries;
const getCountryName = () => {
const codes = props.data;
if(countriesList.length === 0) return <></>;
const borderCountries = [];
codes.map((code) => {
const borderCountry = countriesList.filter((country) =>
country.cca3.includes(code)
);
borderCountries.push(borderCountry);
});
// console.log(borderCountries);
if (props.data.length === borderCountries.length) {
const borderName = borderCountries.map((border) =>
console.log(border[0].name.common)
);
return borderName
}
};
Try this, you forgot to wait for the call to finish.

React, unexpected multiple result when using map and fetch

I am building Weather App, my idea is to save city name in localStorage, pass a prop to child component, then iterate using map and display each in seperate child of the first child
The problem is that displayed data doubles/triples on render(depending on component when render occurs) so when I have for example city London and add city Berlin it will render:
London,London,Berlin
The problem is not in AddCity component, it's working correctly but in this mix of asynchronous setState/fetching and maping
Please see the code below
App(parent component)
const App = () => {
const [cities, setCities] = useState([]);
const addCity = (newCity)=>{
console.log('adding')
setCities([...cities, newCity]);
let cityId = localStorage.length;
localStorage.setItem(`city${cityId}`, newCity);
}
useEffect(() => {
loadCityFromLocalStore()
}, [])
const loadCityFromLocalStore =()=>{
setCities([...cities, ...Object.values(localStorage)])
}
return (
<div>
<Header />
<AddCity addCity={addCity}/>
<DisplayWeather displayWeather={cities}/>
</div>
)
}
DisplayWeather (first child)
const DisplayWeather = ({displayWeather}) => {
const apiKey = '4c97ef52cb86a6fa1cff027ac4a37671';
const [fetchData, setFetchData] = useState([]);
useEffect(() => {
displayWeather.map(async city=>{
const res =await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=>[...fetchData , data]));
})
}, [displayWeather])
return (
<>
{fetchData.map(data=>(
<ul>
<Weather
data={data}/>
</ul>
))}
</>
)
}
Weather component
const Weather = ({data}) => {
return (
<li>
{data.name}
</li>
)
}
It looks like the problem comes from calling setFetchData for cities that you already added previously.
One easy way to fix it would be to store fetch data as an object instead of a dictionary so that you just override the data for the city in case it already exists (or maybe even skip the fetch as you already have the data).
For example:
const [fetchData, setFetchData] = useState({});
useEffect(() => {
displayWeather.map(async city=>{
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=> ({...fetchData, [city]: data})));
})
}, [displayWeather])
Then, to map over fetch data you can just use Object.values:
return (
<>
{Object.values(fetchData).map(data=>(
<ul>
<Weather
data={data}/>
</ul>
))}
</>
)
If you want to skip already fetched cities you can do something like this instead:
useEffect(() => {
displayWeather.map(async city=>{
if (!fetchData[city]) {
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=> ({...fetchData, [city]: data})));
}
})

Very simple react destructuring issue with a custom hook

I'm amazed that I can't solve this myself, but I'm having a basic issue here. Essentially I just want to destructure the user variable in the useSetWelcome hook to prevent the use of verbose chaining such as user.user.email - for instance, const { email } = user does not work and instead needs user.user.
I tried changing from const useSetWelcome = user => { to const useSetWelcome = ({ user }) => {, but that results in an infinite loop.
Where am I going wrong here? My code demo: https://stackblitz.com/edit/react-b1jroe
And the code:
import React, { useState, useEffect } from 'react';
const joe = {
name: 'Joe',
email: 'joe#bloggs.com'
};
// const useSetWelcome = ({ user }) => { // infinite loop problem
const useSetWelcome = user => {
const [name, setName] = useState(null);
const [welcomeMsg, setWelcomeMsg] = useState('No user detected');
// const { email } = user; // needs user.user
// console.log('user', user);
// console.log('{user}', { user });
// console.log('user.email', user.email); // should be joe#bloggs.com
// console.log('email', email); // should be joe#bloggs.com
console.log('user?.user?.email', user?.user?.email); // works
if (user.name) {
setName(user.name);
setWelcomeMsg('welcome ' + user.name);
}
return { name, welcomeMsg };
};
const App = () => {
// const [user, setUser] = useState(joe); // joe or {joe}? and why?
const [user, setUser] = useState(joe);
console.log('state user', user);
const toggleLogin = user => {
if (user) {
setUser(null);
} else {
setUser(joe);
}
};
const loginMsg = user ? 'Logout' : 'Login';
const Welcome = user => {
const { name, welcomeMsg } = useSetWelcome(user);
return (
<p>
{welcomeMsg} {name}
</p>
);
};
return (
<div>
<Welcome user={user} />
<button onClick={() => toggleLogin(user)}>{loginMsg}</button>
</div>
);
};
export default App;
The problem is, <Welcome /> is a component. A component receives only one parameter, props. So, when you write this: const Welcome = user => {, its actually const Welcome = props => {.
Long story short, change this line to const Welcome = ({ user }) => { (so you destruct user from props) and it will work.
P.S.: You're getting an infinite loop because inside your useSetWelcome hook, you have this condition:
if (user.name) {
setName(user.name)
}
When you use setName, the entire hook rerenders, and the condition is tested again. Again, user.name will exist, and setName will get called again, and again, and forever. To achieve what I think you intended to, you have to improve the condition to something like this:
if (user.name && user.name !== name) {
setName(user.name);
setWelcomeMsg('welcome ' + user.name);
}

Why my hook state doesn´t update correctly?

i don't know how make this guys, i can't update my state with the api array, and if i put it in useEffect i have an error cause i am not sending any data, help me please is my first time using stackoverflow
import React, { useEffect, useState } from "react";
import getTeam from "../Helpers/getTeam";
const selectTeams = [
"Barcelona",
"Real Madrid",
"Juventus",
"Milan",
"Liverpool",
"Arsenal",
];
const Select = () => {
const [team, setTeam] = useState(null);
const [loading, setLoading] = useState(null);
const handleOption = async (e) => {
setLoading(true);
let teamsJson = await getTeam(e.target.value);
let arr = [];
Object.keys(teamsJson).map((teamjs, i) => {
return arr.push(teamsJson[teamjs]);
});
console.log(arr);
console.log(team);
setTeam(arr);
setLoading(false);
};
return (
<div
style={{ background: "skyblue", textAlign: "center", padding: "20px" }}
>
<h1>Equipos Disponibles</h1>
<div>
<select onChange={handleOption}>
<option>Elige tu equipo</option>
{selectTeams.map((selectTeam, i) => {
return <option key={i}>{selectTeam}</option>;
})}
</select>
</div>
{loading ? <h1>suave</h1> : (
team !== null ? (
team.map((newTeam, i) => {
return (
<div>
the items are here
</div>
)
})
) : null
)}
</div>
);
};
export default Select;
i let you my api file down
const getTeam = async (teamName) => {
const url = `https://www.thesportsdb.com/api/v1/json/1/searchteams.php?t=${teamName}`;
const res = await fetch(url);
const team = await res.json();
return team;
};
export default getTeam;
i wanna update my const team with the response of my api call, but it doesn't update it, i dont know what do, please help me
The teamsJson value is an object with a single key and value of some array
{ teams: [...] }
So you are updating your state with a nested array when you push the value into another array.
let arr = [];
Object.keys(teamsJson).map((teamjs, i) => {
return arr.push(teamsJson[teamjs]);
});
Based upon how you want to map your team state array I assume you just want the raw inner array from teamJson.
const { teams } = await getTeam(e.target.value);
setTeam(teams);
Then when you are mapping you can access any of the properties you need.
team.map((newTeam, i) => {
return <div key={i}>{newTeam.idTeam}</div>;
})
I've just tested it & it seems to works just fine.
The only 2 issues seem to be that:
You don't use team anywhere (apart from a console.log statement).
At the moment when you console.log(team); the constant team will (yet) be null for the first time (because it still keeps the initial state).
Here's what I see in React dev tools after picking a random team in the <select>:

Resources