How can I get elements from one component in another one by id or value? - reactjs

I've created a component to create follow and unfollow buttons and now I want to use this component in other components (like Suggestions).
In the suggestions component I want to show only the button that its value is equal to the user.id, but I am only able to get the 5 buttons from the original component.
Is there a way to select only the button that is equal to the user.id?
This is the component that creates the buttons:
render() {
const { users, followingUsers } = this.state
const userId = this.props.user[0].id
return(
<div>
{users.map((user, index) => {
if(userId !== user.id) {
if(followingUsers.includes(user.user_name)) {
return(
<Button key={index} value={user.id} onClick={this.onUnfollow}>Unfollow</Button>
)
} else {
return(
<Button key={index} value={user.id} onClick={this.onFollow}>Follow</Button>
)
}
}
})}
</div>
)
}
}
export default withUser(Unfollowfollow);
Here is the suggestions component:
render() {
const { users } = this.state
const userId = this.props.user[0].id
return (
<div>
<ul>
{users.map((user, index) => {
if(user.id !== userId) {
return (
<Card className="users" key= {index}>
<CardBody>
<CardImg className="picfollowers" top width="9%" src={user.image} />
<CardTitle onClick={() => this.handleClick(user.id)}>{user.user_name}</CardTitle>
<Unfollowfollow />
</CardBody>
</Card>
)}
})}
</ul>
</div>
)
}
}
export default withUser(Suggestions);

Related

How can I insert row div for every 3rd entry in props?

I've got a set of dashboards that display in bootstrap cards on a front page and I would like to wrap them in a div with the class row for every 3rd entry. I was thinking about marking my dashboard component with the DB id from props and use a modulus function, but that will cause problems if an ID is deleted
Dashboard component:
export type DashboardProps = {
id: number
title: string
description: string
}
const Dashboard: React.FC<{ dashboard: DashboardProps }> = ({ dashboard }) => {
return (
<>
<div className="col-sm-12 col-lg-4">
<div className="card bg-light h-100">
<div className="card-header">
{dashboard.title}
</div>
<div className="card-body d-flex flex-column">
<p className="card-text">
{dashboard.description}
</p>
<a className="btn btn-info text-center mt-auto"
onClick={() =>
Router.push("/dashboard/[id]", `/dashboard/${dashboard.id}`)
}
>Go to dashboard</a>
</div>
</div>
</div>
</>
)
}
export default Dashboard
Index page:
type Props = {
dashboards: DashboardProps[]
}
export const getServerSideProps: GetServerSideProps = async () => {
const dashboards = await prisma.dashboard.findMany({
orderBy: { id: "asc", },
})
return {
props: JSON.parse(JSON.stringify({ dashboards })),
}
}
const Index: React.FC<Props> = (props) => {
const { data: session, status } = useSession()
if (status === "loading") {
return (
<Spinner />
)
}
if (!session) {
return (
<Layout>
<AccessDenied />
</Layout>
)
}
return (
<Layout>
<h1>Dashboards</h1>
{props.dashboards.map((dashboard) => (
<Dashboard dashboard={dashboard} />
))}
</Layout>
)
}
export default Index
I could also potentially wrap them in a single div with class row, but I would need to enforce a top/bottom margin so the cards don't stack right on top of each other. Any tips to get me rolling on this would be greatly appreciated!
.map provides index, you this to find every 3rd element.
//...
{
props.dashboards.map((dashboard, index) =>
(index + 1) % 3 === 0 ? (
<div>
<Dashboard key={dashboard.id} dashboard={dashboard} />
</div>
) : (
<Dashboard key={dashboard.id} dashboard={dashboard} />
)
)
}

Remove previous content when new one is rendered through a condition (React)

I have a navbar that uses eventKeys to switch between the buttons
const CustomNav = ({ active, onSelect, ...props }) => {
return (
<Nav
{...props}
activeKey={active}
onSelect={onSelect}
style={{ marginBottom: "15px" }}>
<Nav.Item eventKey='all'>All</Nav.Item>
<Nav.Item eventKey='movies'>Movies</Nav.Item>
<Nav.Item eventKey='shows'>Shows</Nav.Item>
<Nav.Item eventKey='people'>People</Nav.Item>
</Nav>
);
};
I did this:
const Content = () => {
if (this.state.active === "all") {
return (
<div>
{trending.results &&
trending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
} else if (this.state.active === "movies") {
return (
<div>
{trendingMovies.results &&
trendingMovies.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
};
Called it here:
return (
<div className='Home'>
<FlexboxGrid justify='center'>
<Panel bordered header='Trending today!'>
<CustomNav
className='customNav'
appearance='subtle'
active={active}
onSelect={this.handleType}
/>
<Content />
<Pagination
{...this.state}
style={{ marginTop: "15px" }}
maxButtons={5}
size='sm'
pages={totalPages}
activePage={this.state.activePage}
onSelect={this.handlePage}
/>
</Panel>
</FlexboxGrid>
</div>
);
}
}
To display the correct data for each tab, but when I'm on the movies tab it shows all the data from the first "all" tab + data on the "movies" tab. I wanna show each data individually corresponding to the correct tab which is controlled by "this.state.active". Tried a switch statement too and that did not work
you are using the arrow syntax
const Content = () => { ... }
and also using this.state variable in your code !!!
if you want to use this.state, then you want to use the class syntax, like
class Content extends React.Component { ... }
but don't mix the two styles.
what you are probably wanting to do is to send the active variable as a prop
try:
const Content = ({active}) => {
if (active === 'all') {
return (...)
} else if (active === 'movies') {
return (...)
}
return null
}
and where you are calling the component you send the active value in as a prop
<Content active={active} />
Note also that you are using the variables trending and trendingMovies and it is unclear where those come from, you may need to send those via props also.
Now you can also leave the if..else logic outside of your Content component like so
const Content = ({myTrending}) => {
return (
<div>
{myTrending.results &&
myTrending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
and then where you call that component you have
<Content
myTrending={active === 'all' ? trending : trendingMovies}
/>
You need to pass active and other variables as props to the Content component, since it doesn't access them otherwise:
const Content = ({active, trending=[], trendingMovies=[]}) => {
if (active === "all") {
return (
<div>
{trending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
} else if (active === "movies") {
return (
<div>
{trendingMovies.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
};
return (
<div className='Home'>
<FlexboxGrid justify='center'>
<Panel bordered header='Trending today!'>
<CustomNav
className='customNav'
appearance='subtle'
active={active}
onSelect={this.handleType}
/>
<Content active={this.state.active} trending={this.state.trending} trendingMovies={this.state.trendingMovies} />
<Pagination
{...this.state}
style={{ marginTop: "15px" }}
maxButtons={5}
size='sm'
pages={totalPages}
activePage={this.state.activePage}
onSelect={this.handlePage}
/>
</Panel>
</FlexboxGrid>
</div>
);
}
}

How on click rerender conmponent in react

When I click on a card, the loadAboutInfo function works through which I transfer data to another component and display it there. But if I click again on the same card, then it is duplicated. How can I fix it?I have check which take card id and then if it the same it render but I click again it render one more card, but i need if it already exist than new card mustn't render
loadAboutInfo=(pokemonValue,pockemonImg,pokemonId)=>{
this.setState(prevState => ({
pokemonValue:[...prevState.pokemonValue, pokemonValue],
pockemonImg,
pokemonId
}))
}
render() {
return (
<div className="wrapper">
<div className="pokemonlist__inner__cards">
<div className="pokemonlist__cards">
{this.state.pokemonList.map((value,index)=>{
let pokemonImgTemplate = this.state.pokemonImgTemplate;
let pokemonId = value.id;
let pockemonImg = pokemonImgTemplate.replace('{id}',pokemonId);
return(
<div className="pokemonListCard" key={index} onClick={()=>this.loadAboutInfo(value,pockemonImg,pokemonId)}>
<PokemonCard
pockemonImg={pockemonImg}
pokemonName={value.name}
pokemonTypes={value.types}
/>
</div>
)
})}
</div>
<PokemonLoadMore
loadMore={this.loadMore}
currentPage={this.state.currentPage}
/>
</div>
</div>
);
}
}
component where i map get data
render() {
return (
<div className="pokemon__about">
{this.props.pokemonValue.map((value,index)=>{
let totalMoves = value.moves.length;
return(
<div className="pokemon__about__wrapper" key={index}>
{this.props.pokemonId == value.id ?
<div className="pokemon__about__inner" key={index}>
<AboutImage
pockemonImg={this.props.pockemonImg}
/>
<AboutName
pockemonName={value.name}
/>
<div className="pokemon__about__table">
<AboutPokemonTypes
pokemonTypes={value.types}
/>
<table>
<AboutPokemonWeight
pockemonWeight={value.weight}
/>
<AboutPokemonMoves
totalMoves={totalMoves}
/>
</table>
</div>
</div>
:
null
}
</div>
)
})}
</div>
);
On the loadAboutInfo you can check if there is already a pokemon with the same id on pokemonValue array, something like this:
loadAboutInfo = (pokemonValue,pockemonImg,pokemonId) => {
// this will get the first element that matches the id
const exists = this.state.pokemonValue.find(pokemon => pokemon.id === pokemonId)
if (!exists) {
this.setState(prevState => ({
pokemonValue:[...prevState.pokemonValue, pokemonValue],
pockemonImg,
pokemonId
}))
}
}
So it will update the state only if the clicked pokemon isn't in the pokemonValue array

React - Warning: Each child in a list should have a unique "key" prop

In this simple React App, I don't understand why I get the following warning message:
Warning: Each child in a list should have a unique "key" prop.
To me it seems that I put the key at the right place, in form of key={item.login.uuid}
How can I get rid of the warning message?
Where would be the right place to put the key?
App.js
import UserList from './List'
const App = props => {
const [id, newID] = useState(null)
return (
<>
<UserList id={id} setID={newID} />
</>
)
}
export default App
List.js
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const renderItem = (item, newID) => {
return (
<>
{newID ? (
// User view
<div key={item.login.uuid}>
<div>
<h2>
{item.name.first} {item.name.last}
</h2>
<p>
{item.phone}
<br />
{item.email}
</p>
<button onClick={() => setID(null)}>
Back to the list
</button>
</div>
</div>
) : (
// List view
<li key={item.login.uuid}>
<div>
<h2>
{item.name.first} {item.name.last}
</h2>
<button onClick={() => setID(item.login.uuid)}>
Details
</button>
</div>
</li>
)}
</>
)
}
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <div>{renderItem(user, true)}</div>
} else {
// List view
return (
<ul>
{resources.map(user => renderItem(user, false))}
</ul>
)
}
}
export default UserList
The key needs to be on the root-level element within the loop. In your case, that's the fragment (<>).
To be able to do that, you'll need to write it out fully:
const renderItem = (item, newID) => {
return (
<Fragment key={item.login.uuid}>
{newID ? (
...
)}
</Fragment>
);
}
(You can add Fragment to your other imports from react).
Note that the fragment isn't actually needed in your example, you could drop it and keep the keys where they are since then the <div> and <li> would be the root element:
const renderItem = (item, newId) => {
return newID ? (
<div key={item.login.uuid}>
...
</div>
) : (
<li key={item.login.uuid}>
...
</li>
)
}
What if you create 2 separate components, one for the user view and one for the list item. That way you only need to pass the user prop. Also, use JSX and pass wht key from there.
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const User = ({user}) => (
<div key={user.login.uuid}>
<div>
<h2>
{user.name.first} {user.name.last}
</h2>
<p>
{user.phone}
<br />
{user.email}
</p>
<button onClick={() => setID(null)}>
Back to the list
</button>
</div>
</div>
)
const ListItem = ({user}) => (
<li key={user.login.uuid}>
<div>
<h2>
{user.name.first} {user.name.last}
</h2>
<button onClick={() => setID(user.login.uuid)}>
Details
</button>
</div>
</li>
)
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <User user={user}</div>
} else {
// List view
return (
<ul>
{resources.map((user, index) => <ListItem key={index} user={user} />)}
</ul>
)
}
}
export default UserList

how to pass props to a component via onClick event?

Here I'm iterating through the list and displaying clickable {expense.createdAt}. When I click on the {expense.createdAt} I want to show its <ExpenseDetails /> on the same page, so I need to pass the props. How do I do that with onClick?
My code below which doesn't display anything.
class ExpenseList extends Component {
render () {
const {expenses} = this.props;
const list = expenses.expenseList.map(expense =>
<Segment clearing key={expense.uid} >
<a href="" onClick={() => {this.passProps(expense)}}>
{expense.createdAt}
</a>
</Segment>
)
const details = function passProps(expense){
return (
<div>
<ExpenseDetails expense={expense}/>
</div>
)
}
return (
<div>
<Grid celled='internally'>
<Grid.Row>
<Grid.Column width={5}>
<div>
<h1>Your Expense List:</h1>
<Segment.Group raised>
{list}
</Segment.Group>
</div>
</Grid.Column>
<Grid.Column width={11}>
<Segment>
{details}
</Segment>
</Grid.Column>
</Grid.Row>
</Grid>
</div>
)
}
}
When I click on{expense.createdAt} the page refreshes. This might be an issue. How can I prevent that?
console.log(this.props.expenses):
Something like this should work. The important concept is that you need to have a parent to both of your subcomponents passing data. In this case, Expenses is the parent. In the parent, you create a function, showItemDetails that you pass down to ExpenseList. That is your clickhandler, which is used to pass back up the index location of the data you want to render. That showItemDetails then uses this.setState to give ItemDetails's the props you want.
import React, {Component} from 'react'
export default class Expenses extends Component {
constructor(props) {
super(props)
this.state = {
expenseItems: []
}
}
showItemDetails(idx) {
const {expenseItems} = this.props.expenses.expenseList[idx]
this.setState({expenseItems})
}
render() {
const createdDates = this.props.expenses.expenseList.map(({createdAt}) => {
return createdAt
})
const expenseListProps = {
showItemDetails: (idx) => this.showItemDetails(idx),
createdDates
}
const itemDetailsProps = {expenseItems: this.state.expenseItems}
return (
<div>
<ExpenseList {...expenseListProps}/>
<ItemDetails {...itemDetailsProps}/>
</div>
)
}
}
class ExpenseList extends Component{
render() {
const expenseList = this.props.createdDates.map((date, idx) => (
<div key={idx}>
<button onClick={() => this.props.showItemDetails(idx)}>
{date}
</button>
</div>
))
return (
<div>
{expenseList}
</div>
)
}
}
class ItemDetails extends Component {
render() {
debugger;
const items = this.props.expenseItems.map(({uid, date, desc, amount}) => (
<div>
{uid}
{date}
{desc}
{amount}
</div>
))
return (
<div>
{items}
</div>
)
}
}

Resources