How can i get all image urls from my nasa api and add them into an image slider - javascript

This is my component of imageslider with button next and previous
I need som help how i can get individual img_src values and add them into another array and them use them in my image slider.
I welcome every solution corresponding to my aproach
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const { loading, error, Images } = ImageList;
useEffect(() => {
dispatch(ListImages());
}, [dispatch]);
var items = [Images.photos];
console.log(Images);
const classes = useStyles();
function Item(props) {
return (
<Paper>
{props.item.map(data => (
<img src={data.img_src} />
))}
{({ onClick, className, style, next, prev }) => {
return (
<Button onClick={onClick} className={classes.button} style={style}>
{next && "Next"}
{prev && "Previous"}
</Button>
);
}}
</Paper>
);
}
return (
<>
{loading ? (
<Loader />
) : error ? (
<h1>{error}</h1>
) : (
<Carousel>
{items.map((item, i) => (
<Item key={i} item={item} />
))}
</Carousel>
)}
</>
);
};
export default ImageSlider;
```

First of all you should move the Item component out of the ImageSlider. It is being redefined every render. You can use localState to keep track of the index.
const useImageIndexer = (maxIndex) => {
const [index, setIndex] = useState(0);
const nextImage = () => {
setIndex((current) => Math.min(maxIndex, current + 1));
};
const prevImage = () => {
setIndex((current) => Math.max(0, current - 1));
};
return [index, nextImage, prevImage];
}
Then to use inside the slider
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const photos = ImageList.Images.photos;
const [index, nextImage, prevImage] = useImageIndexer(photos.length);
const currentPhoto = photos[index];
// Further down in the code
if(loading) {
return (<Loader />);
}
if (error) {
return (<div>Oh no!</div>);
}
return (<div>
<img src={img.src} />
<button onClick={prevImage}>Previous</button>
<button onClick={nextImage}>Next</button>
</div>);
It seemed like you were wrapping photos which sounds like an array inside another array, that doesn't look right.
var items = [Images.photos];

Related

How can I sum up a value from a loop of child components in react?

I have two react components one is Cart and the other is CartItem, Inside Cart I am looping using .map the CartItem. Inside CartItem I have a line total which is price * item.count. How can I sum up all the line totals regardless how many CartItems I have, and return the Total so that I can have the overall value of the cart.
I tried to add a handle total function but its only returning line total of one element not all of them.
Here is the CartItem:
const CartItem = ({item, handleTotal}) => {
var [itemCount, SetItemCount] = useState(1);
var [Linetotal, SetLineTotal] = useState(item.price);
const incrementcount = () => {
SetItemCount(++itemCount);
};
const decrementcount = () => {
SetItemCount(--itemCount);
};
useEffect(() => {
handleTotal(item.id,Linetotal)
},[])
return (
<div>
<Product>
<ProductDetail>
<Image src={item.image} />
<Details>
<ProductName>
<b>Product:</b> {item.name}
</ProductName>
<ProductId>
<b>ID:</b> {item.id}
</ProductId>
<ProductColor color={item.color} />
<ProductSize>
<b>Size:</b> {item.size}
</ProductSize>
</Details>
</ProductDetail>
<PriceDetail>
<ProductAmountContainer>
<Add onClick={() => {incrementcount(); SetLineTotal(item.price*itemCount)}} />
<ProductAmount>{itemCount}</ProductAmount>
<Remove onClick={() => {decrementcount(); SetLineTotal(item.price*itemCount)}} />
</ProductAmountContainer>
<ProductPrice>$ {item.price*itemCount}</ProductPrice>
</PriceDetail>
</Product>
</div>
);
};
export default CartItem;
and Here is the Cart:
const Cart = () => {
var [total, setTotal] = useState([]);
const handleTotal = (id,price) => {
setTotal(id,price);
};
return (
<Container>
<Navbar />
<Announcement />
<Wrapper>
<Title>YOUR BAG</Title>
<Top>
<TopButton>CONTINUE SHOPPING</TopButton>
<TopTexts>
<TopText>Shopping Bag(2)</TopText>
<TopText>Your Wishlist (0)</TopText>
</TopTexts>
<TopButton type="filled">CHECKOUT NOW</TopButton>
</Top>
<Bottom>
<Info>
{IntheCart.map((item) => (
<CartItem key={item.id} item={item} handleTotal={handleTotal} />
))}
<Hr />
</Info>
<Summary>
<SummaryTitle>ORDER SUMMARY</SummaryTitle>
<SummaryItem>
<SummaryItemText>Subtotal</SummaryItemText>
<SummaryItemPrice>$ 80</SummaryItemPrice>
</SummaryItem>
<SummaryItem>
<SummaryItemText>Estimated Shipping</SummaryItemText>
<SummaryItemPrice>$ 5.90</SummaryItemPrice>
</SummaryItem>
<SummaryItem>
<SummaryItemText>Shipping Discount</SummaryItemText>
<SummaryItemPrice>$ -5.90</SummaryItemPrice>
</SummaryItem>
<SummaryItem type="total">
<SummaryItemText>Total</SummaryItemText>
<SummaryItemPrice>$ 80</SummaryItemPrice>
</SummaryItem>
<Button>CHECKOUT NOW</Button>
</Summary>
</Bottom>
</Wrapper>
<Footer />
</Container>
);
};
export default Cart;
You can create updateTotal function in the Card component and once you increse or decrement item in the CardItem update the total value.
const CartItem = ({item, handleTotal, updateTotal}) => {
// code...
const incrementcount = () => {
SetItemCount(++itemCount);
updateTotal(item.price);
};
const decrementcount = () => {
SetItemCount(--itemCount);
updateTotal(-item.price);
};
// code...
updateTotal function.
var [totalPrice, setTotalPrice] = useState(0);
const updateTotal = (value) => {
setTotalPrice(prev => prev + value)
}
Add the updateTotal function to CardItem
<CartItem key={item.id} item={item} handleTotal={handleTotal} updateTotal={updateTotal} />
If you need sub sum to so sub total, you can do like this.
const Cart = () => {
...
var [subSum, setSubSum] = useState(null);
var [totalSum, setTotalSum] = useState(0);
const handleTotal = (id,price) => {
setSubSum({...subSum, id:price});
};
useEffect(()=>{
var sum = 0;
for (const [key, value] of Object.entries(subSum)) {
sum += value;
}
setTotalSum(sum);
},[total])
...
}

How to change the icon of only one particular item of mapped array in reactjs?

I was creating the functionality of pinning and unpinning of particular note, so when the user clicks the thumbtack icon I want that icon of only that particular note changes to a cross icon but when I am clicking on the second notes to pin it then the icon that changed on previous pinned note gets restored to its original form.
I have created the pinning functionality using onPin function but struggling with changing the icon of that particular pinned item.
I want to add icons to pinned items in such a way that previously added close icons stay in their place and do not get updated.
What I tried?
So i created the state variable iconId which is an array so whenever the user clicks pinned icon then new id will be pushed to the iconId array and while displaying the output I put the condition that if the current id is included in iconId array then change icon of all those respective ids in iconId to cross icon, apparently this functionality dint work.
-----------------------App.js--------------------------------
import React, { useState } from "react";
import './App.css';
import Input from './Components/Input';
import Navbar from './Components/Navbar';
import Notesview from './Components/Notesview';
import Notesdata from "./Data/Notesdata";
function App() {
const [data, setData] = useState(Notesdata);
// const [pin, setpin] = useState(true)
const [iconId, seticonId] = useState([])
function handleDelete(id) {
let newData = data.filter((item) => item.id !== id)
setData(newData)
console.log(newData)
console.log(Notesdata)
console.log(0)
}
function handlePost(value) {
// Notesdata.push(value)
// setData(Notesdata)
// // console.log(typeof data)
// console.log(Notesdata)
setData([...data, value]);
}
function onPin(id) {
let index = data.map((item) => {
return item.id
}).indexOf(id)
let arr1 = data.slice(0, index).concat(data.slice(index + 1))
arr1.unshift(data[index])
setData(arr1);
seticonId([...iconId] , id)
console.log(iconId)
}
function handleclose() {
// setpin(!pin)
// seticonId("")
}
return (
<div className="App">
<header className="App-header">
<Navbar />
<Input data={data} handlePost={(value) => handlePost(value)} />
<Notesview handleDelete={handleDelete} Data={data} onPin={onPin} iconId={iconId} handleclose={handleclose} />
</header>
</div>
);
}
export default App;
----------------Noteview function(mapping function)---------------
import React from 'react'
import Notescard from './Notescard'
import "../Styles/Notes.css"
// import { useState } from 'react'
const Notesview = ({ Data, handleDelete, onPin , iconId, handleclose}) => {
return (
<>
<div className='notes'>
{Data && Data.map((item) => {
return <Notescard item={item} handleDelete={handleDelete} onPin={onPin} iconId={iconId} key={item.id} handleclose={handleclose}/>
})
}
</div>
</>
)
}
export default Notesview
-----------------------------Notescard component------------------
import React from "react";
import "../Styles/Notescard.css";
import { FaThumbtack, FaTrashAlt, FaPencilAlt ,FaTimesCircle} from "react-icons/fa";
// import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Notescard = ({ item , handleDelete,onPin,iconId,handleclose, key}) => {
return (
<>
<div className="box">
<div className="content">
<h2 className="item1">{item.title}</h2>
<h4 className="item1"> {item.tagline}</h4>
<p className="item2">{item.description}</p>
</div>
<div className="icons">
{iconId.includes(item.id) ? <FaTimesCircle onClick={handleclose}/> : <FaThumbtack id={item.id} onClick={() => onPin(item.id)}/> }
<FaTrashAlt onClick={() => handleDelete(item.id)}/>
<FaPencilAlt />
</div>
</div>
</>
);
};
export default Notescard;
Issue
You are passing two arguments to the seticonId state updater function.
seticonId([...iconId], id)
The id is never added to the iconId state.
Solution
Use a functional state update to append the id to the array.
seticonId((iconId) => iconId.concat(id));
Code:
const Notescard = ({ item, handleDelete, onPin, iconId, handleclose }) => {
return (
<div className="box">
<div className="content">
<h2 className="item1">{item.title}</h2>
<h4 className="item1"> {item.tagline}</h4>
<p className="item2">{item.description}</p>
</div>
<div className="icons">
{iconId.includes(item.id) ? (
<FaTimesCircle onClick={() => handleclose(item.id)} />
) : (
<FaThumbtack id={item.id} onClick={() => onPin(item.id)} />
)}
<FaTrashAlt onClick={() => handleDelete(item.id)} />
<FaPencilAlt />
</div>
</div>
);
};
...
const Notesview = ({ Data, handleDelete, onPin, iconId, handleclose }) => {
return (
<div className="notes">
{Data.map((item) => {
return (
<Notescard
item={item}
handleDelete={handleDelete}
onPin={onPin}
iconId={iconId}
key={item.id}
handleclose={handleclose}
/>
);
})}
</div>
);
};
...
export default function App() {
const [data, setData] = useState(Notesdata);
const [iconId, seticonId] = useState([]);
function handleDelete(id) {
let newData = data.filter((item) => item.id !== id);
setData(newData);
console.log(newData);
console.log(Notesdata);
console.log(0);
}
function handlePost(value) {
setData([...data, value]);
}
function onPin(id) {
setData((data) => {
const index = data.findIndex((item) => item.id === id);
const arr1 = data.slice(0, index).concat(data.slice(index + 1));
arr1.unshift(data[index]);
return arr1;
});
seticonId((iconId) => iconId.concat(id));
}
function handleclose(id) {
setData((data) => {
const index = data.findIndex((item) => item.id === id);
const insertIndex = data.findIndex((item) => !iconId.includes(item.id));
const arr1 = data.slice(0, index).concat(data.slice(index + 1));
arr1.splice(insertIndex - 1, 0, data[index]);
return arr1;
});
seticonId((iconId) => iconId.filter((elId) => elId !== id));
}
return (
<div className="App">
<Input data={data} handlePost={(value) => handlePost(value)} />
<Notesview
handleDelete={handleDelete}
Data={data}
onPin={onPin}
iconId={iconId}
handleclose={handleclose}
/>
</div>
);
}

To do list making with React hooks

I'm a bit new to React. I'm trying to make simple To do list with react hooks and struggling to make "delete all button". I thought it could be work to using setState [] or return []
but it didn't work...
and also it's showing an error.
TypeError: tasks.map is not a function
Does anyone know how it figure out?
Here is my code
import React, {useState} from 'react'
let INITIAL_TASK = {
title: 'React',
doing: false,
}
const App = () => {
const [tasks, setTasks] = useState([INITIAL_TASK])
const [task_title, setTask_title] = useState('')
const handleTextFieldChanges = e => {
setTask_title(e.target.value)
}
const resetTextField = () => {
setTask_title('')
}
const isTaskInclude = () => {
return tasks.some(task => task.title === task_title)
}
const addTask = () => {
setTasks([...tasks, {
title: task_title,
doing: false,
}])
resetTextField()
}
const deleteTask = (task) => {
setTasks(tasks.filter(x => x !== task))
}
const deleteAllTask = () => {
-------------
}
const handleCheckboxChanges = task => {
setTasks(tasks.filter(x => {
if (x === task) x.doing = !x.doing
return x
}))
}
return (
<React.Fragment>
<Container component='main' maxWidth='xs'>
<CssBaseline/>
<Box
mt={5}
display='flex'
justifyContent='space-around'
>
<TextField
label='title'
value={task_title}
onChange={handleTextFieldChanges}
/>
<Button
disabled={task_title === '' || isTaskInclude()}
variant='contained'
color='primary'
onClick={addTask}
href=''
>
add
</Button>
<Button
// disabled={task_title === '' || isTaskInclude()}
variant='contained'
color='secondary'
onClick={deleteAllTask}
href=''
>
all delete
</Button>
</Box>
<List
style={{marginTop: '48px'}}
component='ul'>
{tasks.map(task => (
<ListItem key={task.title} component='li'>
<Checkbox
checked={task.doing}
value='primary'
onChange={() => handleCheckboxChanges(task)}
/>
<ListItemText>{task.title}</ListItemText>
<Button
href=''
onClick={() => deleteTask(task)}
>
delete
</Button>
</ListItem>
))}
</List>
</Container>
</React.Fragment>
)
}
export default App
You can try doing below
const deleteAllTask = () => {
setTasks([]);
};
or if you want it to set to initial value, you can do below
const deleteAllTask = () => {
setTasks([INITIAL_TASK]);
};

React hook callback from child to parent

I have this child component called TodoList
const TodoItem = ({ checked, children }) =>
(<TouchableOpacity
style={{ backgroundColor: checked && 'red'}}>
{children}
</TouchableOpacity>
);
const TodoList = props => {
const {
options = [],
onSelect,
...rest
} = props;
const [selectedOptionIndex, setSelectedOptionIndex] = useState(null);
useEffect(() => {
onSelect(options[selectedOptionIndex]);
}, [onSelect, options, selectedOptionIndex]);
const renderItem = (o, index) => {
return (
<TodoItem
key={o + index}
onPress={() => setSelectedOptionIndex(index)}
checked={index === selectedOptionIndex}>
{index === selectedOptionIndex && <Tick />}
<Text>{o}</Text>
</TodoItem>
);
};
return (
<View {...rest}>{options.map(renderItem)}</View>
);
};
export default TodoList;
And I have a parent component called Container
export default function() {
const [item, setItem] = setState(null);
return (
<Screen>
<TodoList options={[1,2,3]} onSelect={(i) => setItem(i)} />
</Screen>
)
}
I want to have a callback from child component to parent component using onSelect whenever a TodoItem is selected. However, whenever the onSelect is called, my TodoList re-renders and my selectedOptionIndex is reset. Hence, my checked flag will only change to true briefly before resetting to false.
If I remove the onSelect callback, it works fine. But I need to setState for both child and parent. How do I do that?
It's hard to tell why thats happening for you, most likely because the container's state is changing, causing everything to rerender.
Something like this should help you out, though.
const { render } = ReactDOM;
const { useEffect, useState } = React;
const ToDoItem = ({checked, label, onChange, style}) => {
const handleChange = event => onChange(event);
return (
<div style={style}>
<input type="checkbox" checked={checked} onChange={handleChange}/>
{label}
</div>
);
}
const ToDoList = ({items, onChosen}) => {
const [selected, setSelected] = useState([]);
const handleChange = item => event => {
let s = [...selected];
s.includes(item) ? s.splice(s.indexOf(item), 1) : s.push(item);
setSelected(s);
onChosen(s);
}
return (
<div>
{items && items.map(i => {
let s = selected.includes(i);
return (
<ToDoItem
key={i}
label={i}
onChange={handleChange(i)}
checked={s}
style={{textDecoration: s ? 'line-through' : ''}}/>
)
})}
</div>
);
}
const App = () => {
const [chosen, setChosen] = useState();
const handleChosen = choices => {
setChosen(choices);
}
return (
<div>
<ToDoList items={["Rock", "Paper", "Scissors"]} onChosen={handleChosen} />
{chosen && chosen.length > 0 && <pre>Chosen: {JSON.stringify(chosen,null,2)}</pre>}
</div>
);
}
render(<App />, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
Turned out my top-level component Screen is causing this re-render. In my Screen functional component, I have this piece of code before the return
const Content = scroll
? contentProps => {
const { style: contentContainerStyle } = contentProps;
return (
<ScrollView {...contentContainerStyle}>
{contentProps.children}
</ScrollView>
);
}
: View;
return (
<Content>{children}</Content>
)
And it somehow (not sure why) causes the children to re-render every time my state changes.
I fixed it by removing the function and have it simply returning a View
const Content = scroll ? ScrollView : View;

How to make collapsible list in sidebar

I'm trying to make collapsible list in sidebar. On click i'm changing "isOpen" state and depending on this state I'm displaying or hiding sub links. The problem is that all sub links opening at same time .
Check sandbox here: https://codesandbox.io/s/infallible-moore-h16g6
const Sidebar = ({ title, children, data, opened, ...attrs }) => {
const [isOpen, setTriger] = useState(false);
const handleClick = idx => {
setTriger(!isOpen)
};
return (
<SidebarUI>
{data.map((item, idx) => {
return typeof item.data === "string" ?
<div key={idx} >{item.name}</div>:
<Fragment key={idx}>
<div onClick={() => handleClick(idx)}>{item.name}</div>
{ item.data.map((subs, ids) => {
return <Test isOpen={isOpen} key={ids}>++++{subs.name}</Test>;
})}
</Fragment>
})}
</SidebarUI>
);
};
Try creating an object with the state of the collapsed elements, like this:
const Sidebar = ({ title, children, data, opened, ...attrs }) => {
const [collapseElements, setCollapse] = useState({});
const handleClick = idx => {
const currentElements = Object.assign({}, collapseElements);
setCollapse({ ...currentElements, [idx]: !collapseElements[idx] });
};
return (
<SidebarUI>
{data.map((item, idx) => {
return typeof item.data === "string" ? (
<div key={idx}>{item.name}</div>
) : (
<Fragment key={idx}>
<div onClick={() => handleClick(idx)}>{item.name}</div>
{item.data.map((subs, ids) => {
return (
<Test isOpen={collapseElements[idx]} key={ids}>
++++{subs.name}
</Test>
);
})}
</Fragment>
);
})}
</SidebarUI>
);
};
export default Sidebar;
Checkout the sandbox.
Let me know if it helps.
Edit:
I made a new codesandbox wich I added some transitions. Now open and closing are smooth.
Check this codesandbox
Now it opens and closes.
What you have to do is keep what index you clicked and only display the children when it's the same index.
I also added a way to close and open.
Here is doing that with the code from your question.
const Sidebar = ({ title, children, data, opened, ...attrs }) => {
const [openedIndex , setTriger] = useState(false);
const handleClick = idx => {
// this ternary makes it possible to open and close
setTriger(idx === openedIndex ? -1 : idx)
};
return (
<SidebarUI>
{data.map((item, idx) => {
return typeof item.data === "string" ?
<div key={idx} >{item.name}</div>:
<Fragment key={idx}>
<div onClick={() => handleClick(idx)}>{item.name}</div>
{// here you check if the idx is the same as the opened one}
{// before showing the data of the item}
{idx === openedIndex && item.data.map((subs, ids) => {
return <Test isOpen={true} key={ids}>++++{subs.name}</Test>;
})}
</Fragment>
})}
</SidebarUI>
);
};
You can solve this without using state. Try changing to this
<Fragment key={idx}>
<div class="sidebar-item" onClick={e => openSidebar(e)}>
{item.name}
</div>
{item.data.map((subs, ids) => {
return (
<div className="sidebar-subitem" key={ids}>
++++{subs.name}
</div>
);
})}
</Fragment>
Toggle class on click
function openSidebar(e) {
e.preventDefault();
e.target.classList.toggle("open");
}
Add css
.sidebar-subitem {
display: none;
}
.sidebar-item.open + .sidebar-subitem {
display: block;
}
This is of course not better than #axeljunes but will work too, so I've maintained a separate list of toggled id's and based on that it will toggle.
Also this is my first time using hooks so bare me(feel free to correct me)
const Sidebar = ({ title, children, data, opened, ...attrs }) => {
//const [isOpen, setTriger] = useState(false);
const [list, setList] = useState([]);
const handleClick = idx => {
//setTriger(!isOpen);
if(!list.includes(idx))
setList([...list,idx]);
else{
const newList = list.filter(e => e!==idx);
setList(newList);
}
};
return (
<SidebarUI>
{data.map((item, idx) => {
return typeof item.data === "string" ? (
<div key={idx}>{item.name}MAIN</div>
) : (
<Fragment key={idx}>
<div onClick={() => handleClick(idx)}>{item.name}IN</div>
{item.data.map((subs, ids) => {
return (
<Test isOpen={list.includes(idx)} key={ids}>
++++{subs.name}SIDE
</Test>
);
})}
</Fragment>
);
})}
</SidebarUI>
);
};
export default Sidebar;

Categories