How to set the default value to props - javascript

The below code fetch the URL data and retrieve All the film list with the duration in seconds
But I want to list out only the films which have duration more than 5000 secs
*The component should have a prop minDuration and only films with a duration more than this value should be listed; the default value for this prop should be 5000
Could you please clarify how to solve this
import React, { useState, useEffect } from "react";
export default function App() {
const [films, setFilms] = useState([]);
useEffect(() => {
const listOfFilms = async () => {
const response = await fetch(
"https://api.flixpremiere.com/v1/films/filter/now_showing?limit=10"
);
console.log("here", response.data);
const jsonresponse = await response.json();
console.log("here11", jsonresponse.films);
setFilms(jsonresponse.films);
};
listOfFilms();
}, []);
return (
<div className="App">
<h1>Film Title</h1>
{console.log("films", films.slug, films.films)}
{films.map((film, index) => (
<ul>
<li key={film.title}>
{film.title} {`(${film.duration_seconds})`} </li>
</ul>
))}
</div>
);
}

I'm going to answer the question as it's written, which is "How to set the default value to props".
const Component = ({ property = 5000 }) => { ... }
If you want to only show certain items based on criteria, use the filter function.

The only one way is use default parameter in your function component.
const Component = ({ iNeedDefaultValue = "foo" }) => { ... }
The rest of is deprecated (words of "father of react").
Because defaultProps on functions will eventually get deprecated.
See: https://twitter.com/dan_abramov/status/1133878326358171650

for setting default props for a component, you can use of defaultProps property
class Tooltip extends React.Component {
// ...
}
Tooltip.defaultProps = {
delay: 100,
}

Related

Prevent losing data when refreshing on a different route - react

I wanted to prevent losing state on page refresh while being on a different route path. Im curious why the first example does not work. From what i understand when app mounts first thing that gonna render is component itself and then useEffects run. Since i got 3 here, first fetches and saves the data to the invoiceList state and then next useEffect that run should fill localStorage key with invoiceList state data. The last one obviously retrieve the data.
The second one does fill the "invoiceData" localStorage key with an empty array. Why is this happening if the invoiceList state already have the data after the first useEffect?
The second example that i provided works. I removed second useEffect and set localStorage key in the first useEffect with response data that i get from fetch.
I also wonder if im doing everything correct here. Any feedback appreciated :)
First example (not working):
import { ReactElement, useEffect, useState } from "react";
import { Outlet } from "react-router-dom";
import { Bar } from "../../components/Bar/Bar";
import { Invoice } from "./Root.utils";
type Props = {};
const Root = (props: Props): ReactElement => {
const [invoiceList, setInvoiceList] = useState<Invoice[]>([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch("./data.json");
const data = await response.json();
setInvoiceList(data);
};
fetchData();
}, []);
useEffect(() => {
window.localStorage.setItem("invoiceData", JSON.stringify(invoiceList));
}, []);
useEffect(() => {
setInvoiceList(
JSON.parse(window.localStorage.getItem("invoiceData") || "[]")
);
}, []);
return (
<div>
<Bar />
<Outlet context={{ invoiceList }} />
</div>
);
};
export default Root;
Second example (working):
import { ReactElement, useEffect, useState } from "react";
import { Outlet } from "react-router-dom";
import { Bar } from "../../components/Bar/Bar";
import { Invoice } from "./Root.utils";
type Props = {};
const Root = (props: Props): ReactElement => {
const [invoiceList, setInvoiceList] = useState<Invoice[]>([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch("./data.json");
const data = await response.json();
window.localStorage.setItem("invoiceData", JSON.stringify(data));
setInvoiceList(data);
};
fetchData();
}, []);
useEffect(() => {
setInvoiceList(
JSON.parse(window.localStorage.getItem("invoiceData") || "[]")
);
}, []);
return (
<div>
<Bar />
<Outlet context={{ invoiceList }} />
</div>
);
};
export default Root;
The first example is never storing the data into the localStorage because the fetch is an asynchronous function that and you are writing basically always the empty array into your localStorage.
The order of execution in the first example will be:
fetchData called
window.localStorage.setItem("invoiceData", JSON.stringify(invoiceList)); <- still empty array
setInvoiceList(JSON.parse(window.localStorage.getItem("invoiceData") || "[]"));
response.json() called
setInvoiceList(data); called
I would also recommend to improve your code a little like that:
import React, { useEffect, useState } from "react";
import { Outlet } from "react-router-dom";
import { Bar } from "../../components/Bar/Bar";
import { Invoice } from "./Root.utils";
const Root: React.FC = () => {
const [invoiceList, setInvoiceList] = useState<Invoice[]>([]);
useEffect(() => {
setInvoiceList(
JSON.parse(window.localStorage.getItem("invoiceData") || "[]")
);
const fetchData = async () => {
const response = await fetch("./data.json");
const data = await response.json();
window.localStorage.setItem("invoiceData", JSON.stringify(data));
setInvoiceList(data);
};
fetchData();
}, []);
return (
<div>
<Bar />
<Outlet context={{ invoiceList }} />
</div>
);
};
export default Root;
You can use the Link component from react-router and specify to={} as an object where you specify pathname as the route to go to. Then add a variable e.g. data to hold the value you want to pass on. See the example below.
Using the <Link /> component:
<Link
to={{
pathname: "/page",
state: data // your data array of objects
}}
>
Using history.push()
this.props.history.push({
pathname: '/page',
state: data // your data array of objects
})
Using either of the above options you can now access data on the location object as per the below in your page component.
render() {
const { state } = this.props.location
return (
// render logic here
)
}

React: How to avoid duplication in a state array

I am making MERN social media app.
I want to show all the friends of the current user in a list in SideBar.jsx .
Home.jsx (parent of Sidebar.jsx)
import React, { Component } from "react";
import { Person } from "#material-ui/icons";
import Topbar from "../../components/topbar/Topbar";
import Sidebar from "../../components/sidebar/Sidebar";
import Feed from "../../components/feed/Feed";
import Rightbar from "../../components/rightbar/Rightbar";
import "./home.css";
export default function Home() {
return (
<>
<Topbar />
<div className="homeContainer">
<Sidebar />
<Feed />
<Rightbar />
</div>
</>
);
}
SideBar.jsx
import "./sidebar.css";
import React, { Component, useContext, useState } from "react";
...
import { axiosInstance } from "../../config";
export default function Sidebar() {
const { user } = useContext(AuthContext);
const [followings, setFollowings] = useState([]);
const followingsList = user.followings;
useEffect(() => {
const fetchFollowings = async () => {
followingsList.map(async (id) => {
try {
const theUser = await axiosInstance.get(`/users?userId=${id}`);
if (followings.includes(theUser.data)) {
} else {
setFollowings((prev) => [...prev, theUser.data]);
}
} catch (error) {
console.log(error);
}
});
};
fetchFollowings();
}, [user]);
return (
<div className="sidebar">
.....
<ul className="sidebarFriendList">
{followings.map((u) => (
<CloseFriend key={u._id} user={u} />
))}
</ul>
</div>
</div>
);
}
For example, in this case, in the state "followings", there are 2 user objects.
So, the line
followings.map((u) => (...
should only show 2 entries.
However, the result is below.
As you can see, it is showing each friend twice.
I tired to check if a user object already exists in followings by doing
if (followings.includes(theUser.data)) {
} else {
setFollowings((prev) => [...prev, theUser.data]);
}
But this is not working.
How can I make sure that it only shows each user once?
I want it to be like this
Any help would be greatly appreciated. thank you
This is happening because it seems that your useEffect method is being fired two times (probably because you are using React.StrictMode) and you are setting the state inside the .map method (that is not good because you trigger a new render each time you call the setState).
What I would recommend you to do, is to remove the setState from the .map method and just set the new state after you format your data. So it would be something like this:
const newFollowings = followingsList.map(async (id) => {
try {
const theUser = await axiosInstance.get(`/users?userId=${id}`);
return theUser.data;
} catch (error) {
console.log(error);
}
});
setFollowings(newFollowings);
Probably you would have to add a filtering to the array in case there are some errors (because on errors the mapped value would be undefined):
.filter(data => data);
When you are using the .map function with async/await Promise.all usually always does the trick. Instead of pushing the state on every iteration you collect the followers list and set the state when all your fetching is done. I did not test it yet, but I hope it works.
const followingsList = user.followings;
useEffect(() => {
const fetchFollowings = async () => {
const list = await Promise.all(followingsList.map(async (id) => (
await axios.get('/user?userId=' + id);
)));
setFollowers(list);
};
fetchFollowings();
}, [user]);
Note: let me know if it works, if not I'll do a little sandbox on my own

Await result of API call and update list component

I am calling a REST API and return chat rooms. Those get returned fine, as I can see in the payload of the object via the console.
Now I want to display this in a list.
I was able to display a list and download the chat rooms, but not combine both. This is what I did:
import * as React from 'react';
export default function ChatRoomList({param1}) {
var x = getChatRoom(param1)
console.log(x)
return (
<div>
<li>{param1}</li>
<li> asd </li>
<li> asd </li>
</div>
);
}
async function getChatRoom(status) {
// status could be either 'open' or 'room'
var dict = {
chatType: status,
};
var adminChats = await callKumulosAPIFunction(dict, 'getAdminChat')
return adminChats
}
Now I did try to simply await the first getChatRoom(param1) call. But I can only await inside an async function. I then tried to add the async keyword to the export function, but this would crash the whole app.
How would the workflow be here? And how would I map the result from getChatRoom onto a listview?
The result of the adminChats (console.log(adminChats)):
You need to use useEffect hook to get remote data:
export default function ChatRoomList({param1}) {
React.useEffect(()=>{
(async () => {
var x = await getChatRoom(param1)
console.log(x)
})()
},[])
return (
<div>
<li>{param1}</li>
<li> asd </li>
<li> asd </li>
</div>
);
}
If the data returned from getChatRoom is an array and you want to show it, you need to save the response in a state, or the component will not re-render:
export default function ChatRoomList({param1}) {
const [chatRooms, setChatRooms] = React.useState([]);
React.useEffect(()=>{
(async () => {
var x = await getChatRoom(param1)
setChatRooms(x)
})()
},[])
return (
<div>
<li>{param1}</li>
{chatRooms.map((chatRoom , index) => {
return <li key={index}>{JSON.stringify(chatRoom)}</li>
})}
</div>
);
}
async function getChatRoom(status) {
// status could be either 'open' or 'room'
var dict = {
chatType: status,
};
var adminChats = await callKumulosAPIFunction(dict, 'getAdminChat')
return adminChats.payload
}
I suggest you to read the documentation about React hooks and lifecycle management:
https://reactjs.org/docs/hooks-intro.html
https://reactjs.org/docs/state-and-lifecycle.html
You need to use useEffect hook to call the API.
Read more about hooks
import * as React from 'react';
function getChatRoom(status) {
const [chats, setChats] = React.useState([]);
const getChart = async (status) => {
// status could be either 'open' or 'room'
const dict = {
chatType: status,
};
const adminChats = await callKumulosAPIFunction(dict, 'getAdminChat');
setChats(adminChats);
};
React.useEffect(() => {
getChart(status)
}, [status])
return { chats };
};
export default function ChatRoomList({param1}) {
const { chats } = getChatRoom(param1)
console.log(chats)
return (
<div>
<li>{param1}</li>
{chats?.map((chatRoom , index) => {
return <li key={index}>{JSON.stringify(chatRoom)}</li>
})}
</div>
);
}
Dependencies argument of useEffect is useEffect(callback, dependencies)
Let's explore side effects and runs:
Not provided: the side-effect runs after every rendering.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Runs after EVERY rendering
});
}
An empty array []: the side-effect runs once after the initial rendering.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Runs ONCE after initial rendering
}, []);
}
Has props or state values [prop1, prop2, ..., state1, state2]: the side-effect runs only when any dependency value changes.
import { useEffect, useState } from 'react';
function MyComponent({ prop }) {
const [state, setState] = useState('');
useEffect(() => {
// Runs ONCE after initial rendering
// and after every rendering ONLY IF `prop` or `state` changes
}, [prop, state]);
}

logging the data but not rendering p tag , why?

I am using firebase firestore and i fetched the data , everything is working fine but when i am passing it to some component only one item gets passed but log shows all the elements correctly.
I have just started learning react , any help is appreciated.
import React, { useEffect, useState } from 'react'
import { auth, provider, db } from './firebase';
import DataCard from './DataCard'
function Explore() {
const [equipmentList, setEquipments] = useState([]);
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
data.docs.forEach(item => {
setEquipments([...equipmentList, item.data()]);
})
}
useEffect(() => {
fetchData();
}, [])
equipmentList.forEach(item => {
//console.log(item.description);
})
const dataJSX =
<>
{
equipmentList.map(eq => (
<div key={eq.uid}>
{console.log(eq.equipment)}
<p>{eq.equipment}</p>
</div>
))
}
</>
return (
<>
{dataJSX}
</>
)
}
export default Explore
You have problems with setting fetched data into the state.
You need to call setEquipments once when data is prepared because you always erase it with an initial array plus an item from forEach.
The right code for setting equipment is
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
setEquipments(data.docs.map(item => item.data()))
}

Map array to get specific value

Im trying to map an array to get a specific value and output it in my PodcastList component.
My json (the red underline is what I want to be viewed in PodcastList.js)
https://itunes.apple.com/se/rss/toppodcasts/limit=100/genre=1314/explicit=true/json
This is my Home component:
import React, { Component } from 'react'
import { fetchPopularPodcasts } from './api'
import PodcastList from './PodcastList'
export default class Home extends Component {
state = {
podcasts: [],
loading: true,
}
async componentDidMount () {
const podcasts = await fetchPopularPodcasts();
this.setState({
podcasts,
loading: false,
})
}
render() {
const { podcasts } = this.state
return (
<div className='container'>
<PodcastList list={podcasts} />
</div>
);
}
}
This is my PodcastList component
import React from 'react'
import { Link } from 'react-router-dom'
import slugify from 'slugify'
const PodcastList = ({ list }) => {
return (
<div className='col-md-12'>
{list.map((pod) => {
return (
<div className='pod-box'>
GET THE LABEL?????
</div>
)
})}
</div>
)
}
export default PodcastList;
This is my Api.js
import Feed from 'feed-to-json-promise'
export async function fetchPopularPodcasts () {
const response = await fetch('https://itunes.apple.com/se/rss/toppodcasts/limit=100/genre=1314/explicit=true/json')
const podcasts = await response.json()
return podcasts.feed.entry
}
export async function fetchPodcast (podId) {
const response = await fetch(`https://itunes.apple.com/lookup?id=${podId}&country=se`)
const podcasts = await response.json()
return podcasts.results
}
export async function fetchPodcastEpisodes (feedUrl) {
const feed = new Feed()
const episodes = await feed.load(feedUrl)
return episodes
}
To get an array containing the label property of each array item object, you can use Array's map method:
let list = [ /* the contents included in your screenshot */ ]
let newList = list.map(pod => {
return pod['im:artist'].attributes.label
})
Note that because of the way the property 'im:artist' is formatted -- i.e., with a colon -- you'll need to access it using bracket notation.
Also, ensure that list is actually an array using the inspector.
Finally, for your case in PodcastList:
const PodcastList = ({ list }) => {
return (
<div className='col-md-12'>
{
list.map(pod => {
return (
<div className='pod-box'>
pod['im:artist'].attributes.label
</div>
)
})
}
</div>
)
}
This assumes all of your React-ness is correct, as in that domain, I'm unfamiliar. (If it's still not working, I would investigate if you need curly braces in your innermost div.)
Also, upon reassessing your code, you'd probably rather use forEach as you're not really trying to create a mapped array; rather, you're trying to gain access to a property for each array element.
Your child component would be set up in the same way as the component
import React from 'react'
import { Link } from 'react-router-dom'
import slugify from 'slugify'
const default class PodcastList extends Component {
_renderList = () => {
let elem = this.props.list && this.props.list.length > 0
(this.props.list.map((pod) => {
<div className='pod-box'>{pod}</div>;
)});
return elem;
}
render() {
return (
<div className='col-md-12'>
{this._renderList()}
</div>
)
}
export default PodcastList;

Categories

Resources