how do i update my app sidebar dynamicaly - javascript

function TinderCards() {
const [people, setPeople] = useState([
{
name: "Model Baby",
url:"https://www.themodelskit.co.uk/wp-
content/uploads/2021/10/shutterstock_1431963185.jpg",
age:22
},
{
name: "Seema Jaswal",
url:"https://static.standard.co.uk/2021/06/14/16/euro_2020_live_seema_jaswal_01-1.jpg?
width=968&auto=webp&quality=50&crop=968%3A645%2Csmart",
age:32
},
{
name: 'Baby',
url: '../assets/IMG_20210811_105110_849.webp'
age: 34
}
]);
I am pullin data from a datebase that is like the data i created above for illustration purposes
useEffect(() => {
const allPeople = query(collection(db, "people"))
onSnapshot(allPeople, (snapshot) => (
setPeople(snapshot.docs.map((doc) => doc.data()))
))
return () => {
second
}
}, [])
I am updating the app with the information from the database
return (
<div>
<div className='tinderCards__cardContainer'>
{people.map((person) => (
<TinderCard className="swipe" key={person.name} preventSwipe={["up, down"]} onClick=
{() => {setPer(person.name)}} >
<div className='card' style={{ backgroundImage: `url(${person.url})`}}>
<h3>{person.name}</h3>
</div>
</TinderCard>
))}
</div>
</div>
)
}
export default TinderCards
but how do i update my sidebar with this infomatiom and have the name and other infomation change, eachtime the name changes in the main app
function RightSidebar() {
return (
<div className='rightSidebar'>
<div className='rightSidebar__contents'>
<h1>About</h1>
<Card className='rightSidebar__card'>
<div className='card__nameContents'>
<CardHeader className='card__nameAge' title = {name} subheader = {age}
avatar = {<VerifiedIcon className='activeIcon verified' />} />
<CardHeader className='card__active' title = "active" avatar=
{<FiberManualRecordIcon className='activeIcon' />} />
</div>
The app is quite small do i really need redux or is there a way to track the changes with useState hook?

How far is the common parent of RightSidebar and TinderCards?
If one level up or not too far, store the people data as state of the common parent, pass the data to right sidebar and a function to update the sidebar to TinderCards.
If common parent is far, you can use the React Context API (or the useContext hook).
You don't need redux for this

Related

How to .Map over different props that are passed into a component?

I'm new to React but hopefully someone can help!
So I've just created a component that takes in a value (via prop) and then .maps over that value creating an Image slider. The props are all an array of objects that contain different values such as :
const Songs = [
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
]
I have been making the same component over and over again because I don't know how to make the 'prop'.map value dynamic. Essentially I don't know how to change the value before the .map each different prop.
Here's an example. I want to make 'Songs'.map dynamic so the new props can replace that so they can also be mapped. Maybe there's another way. Hopefully some can help.
import React from 'react';
import { FaCaretDown } from 'react-icons/fa';
function ImageSlider({Songs, KidsMovies, Movies, TvShows}) {
return (
<>
{Songs.map((image, index) => (
<div className="movie-card">
<img src={'https://image.tmdb.org/t/p/w500' + image.poster_path}
className='movie-img' />
<h5 className='movie-card-desc'>{image.original_title}</h5>
<p className='movie-card-overview'>{movie.overview}</p>
</div>
))}
</>
);
}
export default ImageSlider;
Given your example,
I feel like all you need is render ImageSlides for each array
function ImageSlider({ items }) {
return (
<>
{items.map((item, idx) => (
<div ... key={idx}> // be careful to not forget to put a key when you map components
...
</div>
))}
</>
);
}
When rendering your component
function OtherComponent({ songs, kidsMovies, movies, tvShows }) {
return (
<div>
<ImageSlider items={songs} />
<ImageSlider items={kidsMovies} />
<ImageSlider items={movies} />
<ImageSlider items={tvShows} />
</div>
);
}

How to send a property from an array of object from a child component to the parent component?

I have App, that is the parent component and I have the Child component:
The Child component gets a props called items so it can be reused depending on the data. It the example there is data, data1 and data2.
The thing is that I want to set a cookie from the parent component, to set the cookie I need the property link from data2, but I am already mapping data2 in the Child component.
What can I do to obtain the value of the property link in the parent component to pass it as an arguement here:
<Child
onClick={() =>
handleUpdate('How can I obtain here the string from link of data2?')
}
items={data2}
/>
This is the whole example code:
import * as React from 'react';
import './style.css';
const data = [
{ title: 'hey', description: 'description' },
{ title: 'hey1', description: 'description' },
{ title: 'hey2', description: 'description' },
];
const data1 = [
{ title: 'hey', description: 'description' },
{ title: 'hey1', description: 'description' },
{ title: 'hey2', description: 'description' },
];
const data2 = [
{ title: 'hey', link: 'link/hey' },
{ title: 'hey1', link: 'link/he1' },
{ title: 'hey2', link: 'link/he2' },
];
export default function App() {
const [, setCookie] = useCookie('example');
const handleUpdate = (cookie) => {
setCookie(null);
setCookie(cookie);
};
return (
<div>
<h2>App - Parent</h2>
<Child items={data} />
<Child items={data1} />
<Child
onClick={() =>
handleUpdate('How can I obtain here the string from link of data2?')
}
items={data2}
/>
</div>
);
}
export function Child({ items }) {
return (
<div>
<h2>Child</h2>
<ul>
{items.map((item) => {
return (
<>
<p>{item.title}</p>
<a href={item.link}>Go to title</a>
</>
);
})}
</ul>
</div>
);
}
Thank you!
If you want to get the link from the Child component you can simply add a link parameter in the callback:
<Child
onClick={(link) => handleUpdate(link)}
items={data2}
/>
Then from the Child you just need to call the onClick prop:
export function Child({ items, onClick }) { // here make sure to add the prop while destructuring
<a href={item.link} onClick={() => onClick(item.link)}>Go to title</a>
The map method doesn't change the array that it is called on, it just returns a new array, do the items array doesn't get affected at all here, so you can just call it normally like so:
return (
<div>
<h2>App - Parent</h2>
<Child items={data} />
<Child items={data1} />
<Child
onClick={() =>
handleUpdate(data2[0].link)
}
items={data2}
/>
</div>
);
Also, your Child component needs to accept the onClick function as a prop like so:
export function Child({ items, handleClick }) {
return (
<div onClick={handleClick}>
<h2>Child</h2>
<ul>
{items.map((item) => {
return (
<>
<p>{item.title}</p>
<a href={item.link}>Go to title</a>
</>
);
})}
</ul>
</div>
);
}

React: implementing a router

I tried implementing browser router, but to no success. i'm having trouble with useParams hook, and just the router in general. Looked through multiple posts and i just wasn't able to get it working. I'll post the most barebones code below, hoping someone knows the solution. I removed the traces of the router, since it didn't work.
App.js is currently empty:
const App=()=> {
return (
<Main/>
);
}
Main.jsx is my main element, where components change. There isn't a page change per se, everything is in the main element. values get passed through props into main and written into state, so the useEffect can change visibility of components based on what you chose, first category, then recipe.:
const Main =()=> {
const [showElement, setShowElement] = useState("category");
const [selectedCategory, setSelectedCategory] = useState();
const [selectedRecipe, setSelectedRecipe] = useState();
useEffect(()=> {
if (selectedRecipe) {
setShowElement("recipe")
} else if (selectedCategory) {
setShowElement("recipeSelection")
}
window.scrollTo(0, 0)
}, [selectedCategory][selectedRecipe]);
return (
<>
<Header />
<main className="main">
<div>
<div>
{showElement === "category" &&
<CategoryWindow
passSelectedCategory={setSelectedCategory}
/>
}
</div>
<div>
{showElement === "recipeSelection" &&
<RecipeSelection
value={selectedCategory}
passSelectedRecipe={setSelectedRecipe}
/>
}
</div>
<div>
{showElement === "recipe" &&
<RecipeWindow
value={selectedRecipe}
/>
}
</div>
</div>
</main>
</>
)
}
This is the recipe picker component. For example when i click on curry, i'd like the url to show /food/curry. None od the names are hardcoded, everything comes from a javascript object:
const RecipeSelection =(props)=> {
const recipies = Recipies.filter(x=>x.type === props.value);
return (
<div className="selection-div">
<div className="selection-inner">
{recipies.map(selection =>
<>
<img src={require(`../images/${selection.id}.png`)}
className="selection-single"
key={selection.id}
alt={"picture of " + selection.id}
onClick={()=> props.passSelectedRecipe(selection.id)}
>
</img>
<div className="container-h3"
onClick={()=> props.passSelectedRecipe(selection.id)}
>
<h3 className="selection-h3">{selection.name}</h3>
</div>
</>
)}
</div>
</div>
)
}

React modal custom component not showing the correct data

I have built this modal component using react hooks. However the data that the modal shows when it pops up its incorrect (it always shows the name property for last element in the array).
//Modal.js
import ReactDOM from 'react-dom';
const Modal = ({ isShowing, hide, home_team }) => {return isShowing ? ReactDOM.createPortal(
<React.Fragment>
<div className="modal-overlay"/>
<div className="modal-wrapper">
<div className="modal">
<div className="modal-header">
<a>Home team: {home_team}</a>
<button type="button" className="modal-close-button" onClick={hide}>
</button>
</div>
</div>
</div>
</React.Fragment>, document.body
) : null;}
export default Modal;
// Main component
const League = ({ league, matches }) =>{
const {isShowing, toggle} = useModal();
return (
<Fragment>
<h2>{league}</h2>
{
matches.map((
{
match_id,
country_id,
home_team
}
) =>
{
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
})
}
</Fragment>
)};
This is what matches data set looks like:
[{
match_id: "269568",
country_id:"22",
home_team: "Real Kings"
},
{
match_id: "269569",
country_id:"22",
home_team: "Steenberg United"
},
{
match_id: "269570",
country_id:"22",
home_team: "JDR Stars "
},
{
match_id: "269571",
country_id:"22",
home_team: "Pretoria U"
},
]
I am not sure whats going on because the data seems to be passed fine.
<p>{home_team}</p>
in the main component is showing everytime the expected property, however the Modal always shows the last home_team item in the array (e.g.Pretoria U)
you need to call useModal inside of the map function. otherwise you will open on toggle all Modals and the last one overlaps the others
const HomeTeam = ({ match_id, country_id, home_team }) => {
const {isShowing, toggle} = useModal();
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
}
const League = ({ league, matches }) => (
<Fragment>
<h2>{league}</h2>
{ matches.map((match) => <Hometeam {...match} /> }
</Fragment>
);

React, how to access child's state from parent? no need to update parent's state

Hi I am pretty new to React and having really hard time wrapping my head around this whole state management and passing data through state and props. I do understand that the standard react way is to pass down data in a unidirectional way- from parent to child, which I have done so for all other components.
But I have this component called Book, which changes its 'shelf' state, based on user selection form 'read, wantToRead, currentlyReading, and none'. And in my BookList component which renders Book component, but it needs to be able to read Book's shelf state and render the correct books under sections called 'read, wantToRead, currentlyReading, and none'. And since in this case, Book component is being rendered from BookList component and BookList being the parent, i really cannot understand how to enable BookList to access Book's state?
BookList component:
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import Book from './Book'
class BookList extends Component {
render(){
const { books, shelf } = this.props
return (
<div className="list-books">
<div className="list-books-content">
<div className="list-books-title">
<h1>MyReads</h1>
</div>
<div className="bookshelf">
<h2 className="bookshelf-title">None</h2>
{books.map((book)=> {
console.log(book)
if (shelf === ''){
return <div className="bookshelf-books">
{/* <BookStateless book= {book} /> */}
<Book book = {book} />
{/* <BookStateless book= {book} /> */}
</div>
}
})}
</div>
<div className="bookshelf">
<h2 className="bookshelf-title">Currently Reading</h2>
{books.map((book)=> {
if (shelf === 'currentlyReading'){
return <div className="bookshelf-books">
{/* <BookStateless book= {book} /> */}
<Book book = {book} />
</div>
}
// console.log(this.book.title, this.book.state)
})}
</div>
<div className="bookshelf">
<h2 className="bookshelf-title">Want to Read</h2>
{books.map((book)=> {
if (shelf === 'wantToRead'){
return <div className="bookshelf-books">
{/* <BookStateless book= {book} /> */}
<Book book = {book} />
{/* <BookStateless book= {book} /> */}
</div>
}
// console.log(this.book.title, this.book.state)
})}
</div>
<div className="bookshelf">
<h2 className="bookshelf-title">Read</h2>
{books.map((book)=> {
if (shelf === 'read'){
console.log(shelf)
return <div className="bookshelf-books">
{/* <BookStateless book= {book} /> */}
<Book book = {book} />
</div>
}
// console.log(this.book.title, this.book.state)
})}
</div>
</div>
<div className="open-search">
<Link to="/search">Add a book</Link>
</div>
</div>
)
}
}
export default BookList
Book component:
import React, { Component } from 'react'
// import * as BooksAPI from './BooksAPI'
import Select from 'react-select'
import 'react-select/dist/react-select.css'
class Book extends Component {
state={
// state can be read, none, want to read, or currently reading
shelf: ''
}
handleChange(e){
this.setState({ shelf: e['value'] })
console.log("this?", this)
}
render(){
const { book } = this.props
const { shelf } = this.state
console.log("book", book.state)
const options = [
{ value: 'currentlyReading', label: 'currentlyReading'},
{ value: 'wantToRead', label: 'wantToRead'},
{ value: 'read', label: 'read'},
{ value: 'none', label: 'none'}
]
return (
<li key={book.id}>
<div className="book">
<div className="book-top">
<div className="book-cover" style={{ width: 128, height: 188, backgroundImage: `url("${book.imageLinks.thumbnail}")` }}></div>
<div className="book-shelf-changer">
<Select
value=""
options={options}
onChange={(e)=>this.handleChange(e)}
/>
</div>
</div>
<div className="book-title">{book.title}</div>
<div className="book-authors">{book.authors}</div>
</div>
</li>
)
}
}
export default Book
in my app.js i have:
import React from 'react'
import * as BooksAPI from './BooksAPI'
import './App.css'
import Search from './Search'
import BookList from './BookList'
import Book from './Book'
import { Route } from 'react-router-dom'
class BooksApp extends React.Component {
state = {
books : []
}
componentDidMount(){
BooksAPI.getAll().then((books)=> {
this.setState({ books: books })
// console.log("bookstest",this)
})
}
render() {
return (
<div className="App">
<Route exact path="/" render={()=>(
<Book books={this.state.books} />
)} />
<Route path="/search" render={()=>(
<Search books={this.state.books} />
)} />
<Route path="/BookList" render={()=>(
<BookList books={this.state.books} />
)} />
</div>
)
}
}
export default BooksApp
Right now, when i open the booklist component in browser, i get no books because it's not picking up the state in any of the if statements here:
if (shelf === 'currentlyReading'){
return <div className="bookshelf-books">
}
Thank you so much in advance for reading through and, any help would be much appreciated!
Thank you!
You don't need to "access" the child's state, you can pass a callback handler from the parent to the child and when an event is triggered inside the child you can notify the parent through that event handler (callback).
I'll post a small example:
class Book extends React.Component {
handleClick = e => {
const { bookId, onToggleBook } = this.props;
onToggleBook(bookId);
};
render() {
const { name, isRead } = this.props;
return (
<div className={`${isRead && "read"}`} onClick={this.handleClick}>
<span>{name}</span>
{isRead && <i> - You read me</i> }
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
books: [
{
id: 1,
name: "book 1",
isRead: false
},
{
id: 2,
name: "book 2",
isRead: false
},
{
id: 3,
name: "book 3",
isRead: true
},
{
id: 4,
name: "book 4",
isRead: false
}
]
};
}
onToggleBookStatus = bookid => {
const { books } = this.state;
const nextBookState = books.map(book => {
if (book.id !== bookid) return book;
return {
...book,
isRead: !book.isRead
};
});
this.setState(prevState => ({ books: nextBookState }));
};
render() {
const { books } = this.state;
return (
<div>
<div>My Books</div>
{books.map(book => (
<Book
key={book.id}
isRead={book.isRead}
name={book.name}
bookId={book.id}
onToggleBook={this.onToggleBookStatus}
/>
))}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
.read {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
As you know, to pass something from the parent to the child, you use props. To get something from the child to the parent, you again use props, but this time you pass a function down to the child, and then the child calls that function.
So for example, you would modify the child's handle change function to something like this:
handleChange(e){
if (this.props.onShelfChanged) {
this.props.onShelfChanged(e.value);
}
this.setState({ shelf: e.value })
}
And then in the parent, you'll want to pass an onShelfChanged prop down to the book, so that you can get notified when the value changes. Something like this:
// in the render function
{books.map((book, index) =>
<Book book={book} onShelfChanged={() => this.childBookChanged(index)}
)};
And you'll need to create and fill out the childBookChanged function to do whatever updates you need to do.
One thing to be mindful of is that you don't want to be manually keeping the book and the bookshelf in sync. The Book is tracking some state of its own, and then you're passing that up and probably altering the state of the bookshelf. Keeping these in sync as your application grows can be a headache and a source of bugs. Instead, you should have one piece of code be in charge, which it looks like will likely be the bookshelf (since it's the topmost component that cares about this state). So most likely you'll want to remove the internal state from Book, and instead tell the book what to do via props.
If you need the Book component to sometimes work as a standalone and sometimes work inside a bookshelf, then you may need to do a bit more work to get it to support both a "controlled" and "uncontrolled" implementation, but it's still a good idea to move the state up for the controlled case. You can read more about controlled and uncontrolled components here and here

Categories

Resources