I have a navbar component with a search bar and the main page component. When an npm package name is inputted in the search bar it should render the main page component with the details of the package using api.npms.io.
App.jsx
function App() {
const [input, setInput] = useState('react');
return (
<div className="App">
<Navbar setInput={setInput} />
<div className="sections">
<Main input={input} />
</div>
</div>
);
}
export default App;
navbar
function Navbar({ setInput }) {
const inputRef = useRef();
const submitHandler = () => {
setInput(inputRef.current.value);
inputRef.current.value = '';
};
return (
<div className="Navbar" id="Navbar">
<div className="logo">
<img src={logo} alt="logo" />
</div>
<div className="line"></div>
<form onSubmit={submitHandler}>
<div className="search">
<img src={search} alt="search" onClick={submitHandler} />
<input
placeholder={'Search for a NPM package'}
name="name"
type="text"
ref={inputRef}
id="name"
></input>
</div>
</form>
</div>
);
}
export default Navbar;
Main.jsx
function Main({ input }) {
const [packageInfo, setPackageInfo] = useState(null);
const [loading, setLoading] = useState(true);
const fetchPackageInfo = async (input) => {
const response = await fetch(`https://api.npms.io/v2/package/${input}`);
const data = await response.json();
setPackageInfo(data);
setLoading(false);
};
useEffect(() => {
fetchPackageInfo(input);
}, [input]);
return (
<div>
{loading ? (
<Loader />
) : (
<div className="Main" id="Main">
<div className="row">
<Summary
heading={packageInfo.collected.metadata.name}
version={packageInfo.collected.metadata.version}
description={packageInfo.collected.metadata.description}
license={packageInfo.collected.metadata.license}
npm={packageInfo.collected.metadata.links.npm}
github={packageInfo.collected.metadata.links.homepage}
downloads={Math.trunc(
packageInfo.evaluation.popularity.downloadsCount
).toLocaleString()}
keywords={packageInfo.collected.metadata.keywords.join(', ')}
/>
</div>
</div>
)}
</div>
);
}
export default Main;
When I input the value in the search bar and then click on the image or the search icon, it gives me the expected output which is to render the details of the package but while the data is being fetched from the API it does not show the loader.
The next problem is when I submit the inputted value it renders the details of the default package which is react and not the package name entered in the search bar.
Any help would be beneficial. Also, I am new to React and am not sure if I am managing state the right way.
I fixed the problem by adding setLoading(true) before fetching the API and also by adding e.preventDefault() in the submitHandler() function
Related
I have been trying to find a way so that when I click a button the next movie in the array shows up on the screen. I am new to react so please forgive my code. I think my problem is in how I am fetching data from the external site. I am not sure how/when I should load in the data so that it functions most effeciently. Any help or tips would be greatly appreciated
function App() {
const [items,setItems] = useState([]);
async function getItems() {
const response = await fetch('https://etbd.tech/nuspljr_334360/csv2json.php');
const data = await response.json();
setItems([...data]);
}
useEffect(() => {
getItems();
},[]);
async function loadData() {
const response = await fetch('https://etbd.tech/nuspljr_334360/sread.php?f=imdb_top_1000.csv');
const menu = await response.json();
setItems([...menu]);
}
useEffect(() => {
loadData();
},[]);
function Buttons() {
return (
<div>
<button className="button" onClick={nextMovie}>Next Movie</button>
</div>
)
}
function RenderItem() {
return (
<div>
{items.map((item) => (
<div key={v4()} className='card'>
<section className="description">
<img src={item.Poster_Link} alt="Poster_Image"/>
<section className="title">
<p>{item.Series_Title} ({item.Released_Year})</p>
<p>IMDB Rating: {item.IMDB_Rating}</p>
</section>
<p>{item.Overview}</p>
<p>{item.Genre} - {item.Runtime}</p>
</section>
</div>
))}
</div>
)};
return (
<div className="body">
<h2>Guess that Movie</h2>
<div className="table">
<RenderItem />
<div className="nav">
<Buttons />
</div>
</div>
</div>
);
}
export default App;
Okay, some things missing in your code.
First of all, you are referencing to nextMovie function and did not declare it.
After this, you should think about the logic of your component and how it should behave.
You do have a state for storing your items that you are fetching.
Now you need another state to store some kind of logic to display the current movie and when you click the next movie button, to update this state with the next one.
You can set a const [activeIndex, setActiveIndex] = useState(-1);
When you first fetch your items, you can set movieIndex to 0.
And inside your component render item, you will get rid of the items.map; because you don't want to loop inside your array, you just want to show one.
We are now passing the active movie as a prop to RenderItem component and showing the active movie data.
I recommend that you learn more about javascript before trying react code.
You can see a working demo here: https://codesandbox.io/s/optimistic-sid-8morgf?file=/src/App.js
import React, {useState, useEffect} from 'react'
function App() {
const [items,setItems] = useState([]);
const [activeIndex,setActiveIndex] = useState(-1);
async function getItems() {
const response = await fetch('https://etbd.tech/nuspljr_334360/csv2json.php');
const data = await response.json();
setActiveIndex(0);
setItems([...data]);
}
useEffect(() => {
getItems();
},[]);
function Buttons() {
return (
<div>
<button className="button" onClick={nextMovie}>Next Movie - next index {activeIndex + 1}</button>
</div>
)
}
function RenderItem({item}) {
return (
<div>
<div className='card'>
<section className="description">
<img src={item.Poster_Link} alt="Poster_Image" />
<section className="title">
<p>{item.Series_Title} ({item.Released_Year})</p>
<p>IMDB Rating: {item.IMDB_Rating}</p>
</section>
<p>{item.Overview}</p>
<p>{item.Genre} - {item.Runtime}</p>
</section>
</div>
</div>
)};
const nextMovie = () => setActiveIndex((prev) => prev + 1);
return (
<div className="body">
<h2>Guess that Movie</h2>
<div className="table">
{items.length === 0 ? 'Loading...' : (<>
<RenderItem item={items[activeIndex]} />
<div className="nav">
<Buttons />
</div>
</>)}
</div>
</div>
);
}
export default App;
I am new to learning react and am stuck with this doubt. I have a simple button and on click of that button I want to add some text (or any other html) element. The console log statement is getting executed but the div tag is not getting rednered. This is my following code.
function App() {
const executeMe = () => {
console.log("executed")
return(
<div> Clicked here</div>
)
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
</div>
</LocalizationProvider>
);
}
export default App;
I know that I am missing out something which may be very simple. Please help me fix this. Thanks
Your looking at React wrongly, it doesn't work this way. You can do this instead.
import { useState } from "react";
function App() {
const [clicked, setClicked] = useState(false);
const [lines, setLines] = useState([]);
const executeMe = () => setClicked(!clicked);
const onAddLine= () => setLines(lines.concat("New line (Could be unique)"));
return (
<div className="App">
Hello world
{/* METHOD A */}
{!clicked && <button onClick={executeMe }>Click me</button>}
{clicked && <div>Clicked here</div>}
<br />
{/* METHOD B */}
<button onClick={executeMe}>{clicked ? "Clicked here" : "Click me"}</button>
<br />
{/* ADDITIONAL FUN STUFF WITH SEPERATE BUTTON */}
<button onClick={onAddLine}>Add new line</button>
<br />
{lines.map((line, x) => {
return(
<div key = {x}>{x+1} : {line}</div>
);
})}
</div>
);
};
export default App;
You can render that div by using state instead and reset it on the next click.
function App() {
const [showDiv, setShowDiv] = useState(false);
const executeMe = () => {
console.log("executed");
setShowDiv(!showDiv);
};
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showDiv && <div> Clicked here</div>} {/* render div once showDiv state is true */}
</div>
</LocalizationProvider>
);
}
export default App;
You should add a state value to check when the button has been pressed.
Here is more information about how to use useState hook.
function App() {
const [isButtonPressed, setIsButtonPressed] = useState(false);
const executeMe = () => {
console.log("executed");
setIsButtonPressed(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{isButtonPressed && <div>Clicked here</div>}
</div>
</LocalizationProvider>
);
}
export default App;
There are many ways to achieve it.
First React is just JavaScript, most JS code will work within the component.
But some dev might find it not so React which is weird for me :)
So here are the two examples that you might try:
function App() {
const [list, setList] = React.useState([])
const handleAddLine = () => {
const lists = document.getElementById('lists')
const li = document.createElement('li')
li.textContent = 'hey'
lists.append(li)
}
const handleAddLineReactish = () => {
setList(prevList => {
return prevList.concat(<li>hey</li>)
})
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={handleAddLine}>Add</button>
<ul id='lists'></ul>
<button onClick={handleAddLineReactish}>Add Reactish</button>
<ul>
{list.length > 0 && list.map((l, i) => {
return (
<li key={i}>{l}</li>
)
})}
</ul>
</div>
);
}
sandbox URL: https://codesandbox.io/s/funny-sun-7f4epn?file=/src/App.js
For something like this we use a react hook called "useState".
In "useState" we store a something and on the basis of that we do stuff like to show, hide and more.
See the image
you can write that html code in another component and import it into the current file you can make useState to check the value is 'visible' with type 'true/false' to check the state when the button is click.
code example
import React, { useState } from "react";
function App() {
const [showText, setShowText] = useState(false);
const executeMe = () => {
console.log("executed")
setShowText(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showText ? <Text /> : null}
</div>
</LocalizationProvider>
);
}
const Text = () => <div>You clicked the button!</div>;
export default App;
My goal was to fetch posts from Graphcms and populate an array - posts, and populate it into postlist, then the main component will change according to what the user clicks on a post from postlist, I can see the posts array is populated , but when i click on a post on postlist I get the following error
Main.js:22 Uncaught TypeError: Cannot read properties of undefined (reading 'featuredImage')
Below my files
App.js
function App() {
const [selectedPost,setSelectedPost] = useState(0);
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const { posts } = await request(
'https://api-ap-southeast-2.graphcms.com/v2/ckxo1np9m5kw601xpccps4lrn/master',
`
{
posts {
id
title
slug
excerpt
featuredImage
{
url
}
}
}
`
);
console.log("print posts " , posts)
setPosts(posts);
};
fetchPosts();
}, []);
return ( <div className='app'>
<Header/>
{
posts.length>0 && (<>
<Main posts={posts} selectedPost={selectedPost}/>
<PostList posts={posts} setSelectedPost={setSelectedPost} />
</>
)
}
</div>
)
}
export default App;
And the Main.js Component
const Main = ({selectedPost,posts}) => {
const[activePost,setActivePost] =useState(posts[0])
console.log("activePost ", activePost)
useEffect(()=>{
setActivePost(posts[selectedPost])
},[posts,selectedPost])
return (
<div className='main'>
<div className='mainContent'>
<div className='postHighlight'>
<div className='postContainer'>
<img
className='selectedPost'
src= {activePost.featuredImage.url}
alt=''
/>
</div>
</div>
<div className='postDetails' style={{color:'#fff'}}>
<div className='title'>
{activePost.title} </div>
<span className='itemNumber'></span>
<span className='postExcerpt'>{activePost.excerpt}</span>
</div>
<div className='otherDetails'>
</div>
</div>
</div>
)
}
export default Main
And then we have postList.js file
const PostList = ({posts,setSelectedPost}) => {
return (
<div className='postList'>
{posts.map(post=>(
<div onClick={()=>setSelectedPost(post.id)}>
<CollectionCard key={post.slug} title={post.title} excerpt={post.excerpt} imageSrc={post.featuredImage.url}/>
</div>
)) })
</div>
)
}
export default PostList
Based on your app, you are using the index of the selected post.
The error arises from your onclick function. You are passing post.id to setSelectedPost() so you are accessing the posts array incorrectly. Hence, the undefined.
Just use the current index on your map function:
<div className='postList'>
{posts.map((post, index) => (
<div onClick={() => setSelectedPost(index)} key={post.slug}>
<CollectionCard
title={post.title}
excerpt={post.excerpt}
imageSrc={post.featuredImage.url}
/>
</div>
))
}
</div>
I am creating a challenge tracking app in React. I would like to, after clicking on the challenge button and approving it, be able to add it and save it to the local storage (as a value to save the name of the chosen challenge) and later to print it in the dashboard.
Could anyone please help me with that.
I have 3 classes I am working now and will paste them below.
ChooseChallenge.js
function Challange() {
const [isPopped, setPop] = useState(false);
const pop = () => {
setPop(!isPopped);
};
return (
//Fragments
<>
{isPopped && <Dialog />}
<div className="chooseChallenge">
{/* <Leaf/> */}
<h1 className="newchallenge">New Challange</h1>
<hr />
<div className="challanges">
<button className="challangeBtn" onClick={pop}>
Eat Vegetarian (31days)
</button>
<button className="challangeBtn" onClick={pop}>
Take the bike to work (14days)
</button>
<button className="challangeBtn" onClick={pop}>
Recycle your plastic bottles (31days)
</button>
<button className="challangeBtn" onClick={pop} >
Use public transport to commute (31days)
</button>
<button className="challangeBtn" onClick={pop}>
Don't fly an airplane (365days)
</button>
</div>
<br />
</div>
</>
);
}
export default Challange;
Dialog.js
function Dialog (){
const [isOpen, setOpennes] = useState(true);
const Close = () => {
setOpennes(false);
}
const [value, setValue] = React.useState(
localStorage.getItem('challengeName') || ''
);
React.useEffect(() => {
localStorage.setItem('challengeName', value);
}, [value]);
const onChange = event => setValue(event.target.value);
return(
<div className={isOpen ? 'dialogBox' : 'dialogHide'}>
<h3 id="header">Do you accept the challange?</h3>
<div className="approvalButtons">
<button className= "approvalButton" onClick = {Close} value={value} onChange={onChange}> Approve </button>
<button className= "approvalButton" onClick = {Close}> Decline </button>
</div>
</div>
)
}
export default Dialog;
Dashboard.js
export default function Dashboard() {
// const challengelist = document.querySelector('#challange-list')
const [challs, setChalls] = useState([]);
useEffect(() => {
const fetchData = async () => {
var challs = [];
await database
.collection("Challenges")
.get()
.then((snapshot) => {
snapshot.docs.forEach((doc) => {
challs.push(doc.data().ChallengeName);
});
});
setChalls(challs);
};
fetchData();
}, []);
return (
<div className="Dashboard">
<Header />
<div className="circle">
<img id="leafpicture" src={leafpic} alt="eco-picture" />
<div className="textIn">
<h1> You saved </h1>
<h5>0.00 CO2</h5>
</div>
</div>
<div>
<ul id="challange-list">
{challs.map((ch) => (
<li key={ch}>{ch}</li>
))}
</ul>
</div>
<div className="progressbar">
<h3>Track your challenges!</h3>
{testData.map((item, idx) => (
<ProgressBar
key={idx}
bgcolor={item.bgcolor}
completed={item.completed}
/>
))}
</div>
<br />
</div>
);
}
on dialog.js the value of the button starts with an empty string and this value never changes, so you are always storing and empty string.
I'm currently attempting to render components based off of what page the user is currently on. One of the components remains the same and which one is rendered with it will be determined on page location. I'm currently using window.location.pathname but this is not a desired behavior for us.
const Container = reduxForm({ form: 'login' })(LoginForm)
const currentLocation = window.location.pathname
const ExtraButtons = () => (
<div className="row center-xs">
<div className="col-xs-6 center-xs">
<Link to={FORGOT_PASSWORD_ROUTE}>Forgot Password?</Link>
</div>
</div>
)
const Login = ({ loginStart, error, update, search, address,
currentUser }) => (
<div>
<div className="row center-xs">
<div className="col-xs-12">
<img src={logo} className="logo" />
</div>
</div>
<div className="row">
<div className="col-xs">
<Container onSubmit={loginStart} />
{error && (
<InputError
visible={error && error !== null}
errorMessage={error.message}
/>
)}
<ExtraButtons />
</div>
{currentLocation.includes('/login') ? (
<div className="row center-xs start-xs col-xs">
<LocSearch
updateAddress={update}
search={search}
address={address}
/>
</div>
) : currentLocation.includes('/home') ? (
<div className="col-xs">
<EmailSearch />
</div>
) : null}
</div>
</div>
)
const mapStateToProps = state => {
const { submitting, currentUser } = state.user
const { address = '' } = state.locationSearch
const error = state.error
return {
address,
submitting,
error,
currentUser
}
}
const mapDispatchToProps = {
loginStart,
resetError,
update,
search
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)
I think currentLocation is assigned the value when the script is loaded instead of when your component is rendered. that is the root cause. you should define currentLocation in your component.