React dynamically create buttons based on number of results - javascript

I am working in a Spring application that uses react. Currently I have a json that contains several users based on certain criteria. The number of users can vary, but I would like to create several buttons for each user returned that links to the users profile. the url is just '/profile/username'
format of json
[{"user":{"principal":"cat#sitter.com","schedule":null,"appointments":null,"roles":["ROLE_USER"],"attributes":{"principal":"cat#sitter.com","zipcode":"98077","firstname":"cat","password":"abc123","sitterFlag":"true","ownerFlag":"false","lastname":"sitter","username":"catsitter","preferredPet":"Cat"},"momento":"cat#sitter.com"},"password":"$2a$10$ltnL.mFqo7hatj69Ls76xeegjhEX0D4At9m1rlBHbQtDrV8MdSeAS","momento":"cat#sitter.com"},{"user":{"principal":"test#pets.com","schedule":null,"appointments":null,"roles":["ROLE_USER"],"attributes":{"principal":"test#pets.com","zipcode":"98077","firstname":"test","password":"abc123","sitterFlag":"false","ownerFlag":"false","lastname":"pets","username":"testpets"},"momento":"test#pets.com"},"password":"$2a$10$wDhS6Mb8syhC0YIqgVG2qu8J6lA.1T.UprMYwAX6O7Xb3YMhgX3bO","momento":"test#pets.com"},{"user":{"principal":"test#sitter.com","schedule":null,"appointments":null,"roles":["ROLE_USER"],"attributes":{"principal":"test#sitter.com","zipCode":"98077","firstname":"test","password":"abc123","lastname":"sitter","username":"testsitter"},"momento":"test#sitter.com"},"password":"$2a$10$DuIeWFSzhtAjX3lr8xBNiu2kV9kAJ/PQ6pB/EzkB7FkGWfRbwxkzy","momento":"test#sitter.com"},{"user":{"principal":"sit#sitter.com","schedule":null,"appointments":null,"roles":["ROLE_USER"],"attributes":{"principal":"sit#sitter.com","zipCode":"98077","firstname":"sit","password":"abc123","lastname":"sitter","username":"imasitter"},"momento":"sit#sitter.com"},"password":"$2a$10$2NKOQkGZO/jUer3UjNGzdugUhkMV1pJ1eT8NQjSPRto9/cRdm56sO","momento":"sit#sitter.com"},{"user":{"principal":"a#sitter.com","schedule":null,"appointments":null,"roles":["ROLE_USER"],"attributes":{"principal":"a#sitter.com","zipCode":"98077","firstname":"a","password":"abc123","lastname":"sitter","username":"asitter"},"momento":"a#sitter.com"},"password":"$2a$10$8x1uVqR28x5rwNrydieSyu1ILifBJ5n0dUsZI5tJ6MoUWMqXxrmeq","momento":"a#sitter.com"}]
I currently have it working if I hard code for each user:
<div className="container padded">
<div className="row">
<div className="col-6 offset-md-3">
<h2>Suggested Sitters</h2>
<button onClick={() => this.suggestSitter(this.props.user.principal)}>Click</button>
<hr/>
<div>
Sitters:
</div>
<Link to={this.setProfile(this.state.sitter ? this.state.sitter[1].user.attributes.username: ' ')} >
<button type="button">{this.state.sitter ? this.state.sitter[1].user.attributes.username: ' '}</button>
</Link>
</div>
</div>
</div>
the setProfile works like this:
setProfile(theUser) {
return '/profile/' + theUser;
}
Clicking a button will redirect to that user's profile page.
So basically, instead of hardcoding n buttons, I would like to dynamically create n buttons and each will link to '/profile/username/ for each user returned.
suggestSitter function:
suggestSitter(user){
var _this = this;
console.log('user', user);
axios.get('/api/user/suggest_sitter', { params: { principal: user } })
.then(function(response) {
console.log('Response: ' + JSON.stringify(response));
_this.setState({
sitter: response
});
})
.catch(function (e) {
console.log('Error: ' + e);
});
}

You can map the data to an array of Link (provide an unique key for it too):
{this.state.sitter.map((e) => (
<Link key={e.someUniqueProperty} to={this.setProfile(e.user.attributes.username)}>
<button type="button">{e.user.attributes.username}</button>
</Link>
))}

Suppose your data is:
const data = [
{user: {...}, attribute: {...}},
{user: {...}, attribute: {...}},
...
]
Now, you can follow these steps:
Create a stateless/stateful component(depending on your use case): UserButton or any other meaningful name:
const UserButton = () => (
<div className="container padded">
<div className="row">
<div className="col-6 offset-md-3">
/*...Add your remaining JSX....*/
</div>
</div>
</div>
)
Now in your parent component(from where you are actually rendering the data), you can do the following:
renderUserButtons = () => {
return data.map((userData) => (
<UserButton key="Some-unique-id-can-be-anything" PASS_YOUR_PROPS_HERE/>
))
}
render() {
return (
<div>
...
{this.renderUserButtons()}
...
</div>
);
}
Obviously, you don't need multiple components for this, but splitting it into smaller components looks good, easier to maintain and easier to test. Again it's my personal preference. You can use the convention whatever is best for you.

To create any UI component from some array, you can always use map function like below.
Array of JSON Object
let users = [{"name":"ABC"},{"name":"DEF"},{"name":"GHI"}];
Map Function
let userList = users.map((user,index)=>{return (<div>{index} - {user.name}<div>)})
this will give you following output:
0 - ABC
1 - DEF
2 - GHI
in map function user is the reference to one by one users from the array. Index is the key for each value from array (ex. 0,1,2....).
We are returning a JSX object from the return statement.
You can now use userList variable in render's return. Like below..
render(){ let userList = "...."; return(<div>{userList}</div>
I hope this would help.

Related

How to display data according to query string using ReactJs?

I have multiple data & I want to display data as per query string, how can I do that in reactJs?
My code:
const Ladies = () => {
return (
<div>
if('http://localhost:3000/ladies?service=makeup'){
<div>Makeup Content</div>
}
else('http://localhost:3000/ladies?service=hairStyling'){
<div>Hair Styling Content</div>
}
</div>
)
}
Thank You!
I consider this for your url
http://localhost:3000/ladies?service=makeup
In your code
const params = new URLSearchParams(window.location.search)
check if it has the query
params.has('service')?params.get('service'):""
or log it
console.log(params.has('service')?params.get('service'):"")
return will be makeup
I'm not sure but i think it will be string so if you want to use it check if it's equal to "makeup" like so
<div> {params.has('service')&&params.get('service')==="makeup"?"There is makeup":"There is no make up in query strings !"}</div>
Update:
you can put that just like a text to show a div instead, that is a great feature of jsx, do it like this.
<div>
{params.has("service") && params.get("service") === "makeup" ? (
<div>Makeup section</div>
) : params.get("service") === "hairStyling" ? (
<div>hair styling section</div>
) : (
<div>cannot find any query strings</div>
)}
</div>
For more info check out here

Replace blank div card/panel with another one that is clicked in React

I'm new to react and not sure I'm going about this the right way. What is happening is I'm grabbing data from pokemon api turning that data into cards that show up based on game selected. Data gets transferred via prop pokedex. Clicking on a card gets me the information for later storing/use.
Currently I can click the generated cards(from panelComp) and have only 1 show up. It does change based on what I click but does not replace the blank card. I know I will need a statement that stores the card in each div and wont let you go over 6.
What end goal and looking to do is to have 6 blank cards/panels up top as "empty"(grey boxes). I want to fill these with the selected pokemon cards/panels from the ones generated from PanelComp.
Later will be using the selected cards to make a filter based on pokemon types. I know I will also need to add a click event to those cards so I can remove them back to blank. I have tried a few things to no avail any direction would be greatly appreciated as I just cant grasp this for some reason.
Code has placeholders via div emptyBox just so I can lay it out.
import Panel from './Panel';
import './PanelList.css';
const PanelList = ({ pokedex }) => {
const [card, setCard] = useState(null);
const [panelPick, setPanelPick] =useState(null);
const [isSelected, setIsSelected]= useState(false);
useEffect(() => {
setPanelPick(panelComp[card]);
setIsSelected(true);
},[card]);
const panelComp = pokedex.map((pokemon, i) => {
return <Panel
onChange={num => setCard(num)}
panelId={i}
id={pokedex[i].id}
name={pokedex[i].name}
types={pokedex[i].types}
/>
})
const isItSelected = isSelected;
return (
<div>
<div>
<div>
{isItSelected ? (
<div id='Block1'>{panelPick}</div>
) : (
<div className='emptyBox' id='Block1'></div>
)}
</div>
<div className='emptyBox' id='Block2'>{panelPick}</div>
<div className='emptyBox' id='Block3'></div>
<div className='emptyBox' id='Block4'></div>
<div className='emptyBox' id='Block5'></div>
<div className='emptyBox' id='Block6'></div>
</div>
<div>
{panelComp}
</div>
</div>
);
}
export default PanelList;
import React from 'react';
import './Panel.css'
const Panel = ({id, name, types, panelId, onChange, onChildEvent }) => {
const handleEvent = event => {
onChange(panelId)
};
return (
<div className="PNL" onClick={handleEvent}>
<img className='pokeImg' alt='pokemon img' src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png`}/>
<div className='Potext'>
<h2>{name[0].toUpperCase() + name.substring(1)}</h2>
<p>Id: {id}</p>
<p>Type: {
types.map(type =>
type.type.name[0].toUpperCase() + type.type.name.substring(1))
.join(', ')
}</p>
</div>
</div>
);
}
export default Panel;
This is my current code for the component and child in question. I can add more if needed from the parent. Not sure if I'm making this too difficult or not. I have been working at this for a few days and just don't know where to go with it. Really trying to learn as I go which I'm not sure if it helps.
Thank you

How to inject a dinamically created element into an existing div in React JSX?

I have a list of objects photos, from a json data file, that I would like to organize into 3 different <div> columns, but I dont know how to achieve that, here is my broken non-optimized code:
<div className="container">
<div ref={leftColRef} className="left-col" />
<div ref={centreColRef} className="centre-col" />
<div ref={rightColRef} className="right-col" />
{Object.keys(photos).forEach((n, i) => {
const id = photos[n].id;
const thumb = photos[n].thumbnailUrl;
const title = photos[n].title;
const element = (
<Thumbnail id={id} title={title} thumb={thumb} />
);
if (i % 3 === 0) {
leftColRef.current.append(element);
} else if (i % 3 === 1) {
centreColRef.current.append(element);
} else {
rightColRef.current.append(element);
}
// this line works, it idsplays the data but is commented as the data needs to go inside its respective columns
// return <Thumbnail key={id} title={title} thumb={thumb} />;
})}
</div>
The idea is to insert some elements into the left-column when i%3 = 0 and others in the centre-column when i%3 = 1 and so on ...
And a link to my codesandbox
Any help/advise will be much appreciated.
Easiest is probably to prepare the data outside the render function and to render the column one by one.
You should not manipulate the DOM like it's done in jQuery using JSX
Example:
const Component = (props) => {
const filterPhotos = (column) => {
return props.photos.filter((photo,index)=> index%3==column);
}
return <>
<MyColumn photos={filterPhotos(0)}/>
<MyColumn photos={filterPhotos(1)}/>
<MyColumn photos={filterPhotos(2)}/>
</>;
}
First, using ref on div to inject stuff on it is wrong. It's the opposite of how react works.
Like charlies said, I would split the photos in 3 different arrays before the render. Then, you'll be able to do something like this :
<div ref={leftColRef} className="left-col" />
{ photosLeft.map(photo => <Thumbnail key={photo.id} {...photo} />)
</div>
when preparing your data, try to use the same object properties and component props name so you can spread it easily ( {...photo} ).
Note: Also, when rendering an array in react, each child must have a unique key props. It will help react to render on that part of dom if your data change.

React Child Component Loop not redenring

Well I have one of the views from my single page application that is a Quiz, But when a click to generate the Components through a loop based on information on my array state he doesn't render it. I'm using react-router in the index.js maybe this information can help.
Gabarito.js
return(
<div>
<h1 className="display-1">Resposta-#{this.props.chave}-
{this.props.alternativa}</h1>
</div>);
Quiz
state = {
respostas:[10],
gabarito:['Verdadeiro','Falso','Falso','Verdadeiro','Verdadeiro','Falso','Verdadeiro','Falso','Verdadeiro','Verdadeiro'],
correcao:[],
novogabarito: this.eachcomponent
}
alterarevento = (evento,index) =>{
let array = this.state.respostas;
array[index] = evento.target.value;
this.setState({respostas:array});
console.log(this.state.respostas[index])
}
gerargabarito = () =>{
for(let n=0;n<10;n++){
if(this.state.respostas[n]===this.state.gabarito[n]){
this.state.correcao.push('Certa');
}
else{
this.state.correcao.push('Errada');
}
}
console.log(this.state.correcao);
}
eachcomponent = () =>{
return(this.state.correcao.map(resposta=><Gabarito chave={this.state.correcao.indexOf(resposta)} alternativa={resposta}/>));
}
Render of function
<div className="row justify-content-center">
<span id="teste">{this.state.novogabarito}</span>
</div>
</div>
);
}
}
Perhaps I am overlooking something...but it does not look like you are ever invoking your alterarevento and gerargabarito functions. So when you call your eachcomponent your correcao array in your state is still empty so you are not mapping anything.
Before your return statement in your eachcomponent function, try logging correcao to the console to see if it is empty.
A word of caution: you should never manipulate your state directly. So, your this.state.correcao.push('Certa');
line should be:
this.setState({ correcao: [...this.state.correcao, 'Certa'] });

How to apply rules to only certain elements in array

I have this loop in react:
<div>
{this.state.dealersDeck.map(function(card, index){
return <div className="cardFormatDH" key={ index }> {card.rankKey}{card.suit} </div>;
}, this)}
</div>
This goes through an array of objects and then renders them on screen. This is all good except I would like to format it so I only display the contents at certain points. i.e. I'm creating blackjack and I don't want the dealers second card to be visible until the end.
I may have to show more code but was wondering if map had some sort of attribute that I could use.
You could add a boolean prop to each card and render based on that:
<div>
{this.state.dealersDeck.map(function(card, index){
return { card.display &&
<div className="cardFormatDH" key={ index }>{card.rankKey} {card.suit} </div>
}
}, this)}
</div>
You can use basic If...Else statements inside map function as well. Moreover you can write more business logic also to add more functionality.
var cardsHTML = this.state.dealersDeck.map(function(card, index){
if(condition...1){
return <div className="cardFormatDH" key={ index }> {card.rankKey}{card.suit} </div>
}
if(condition...2){
return <div></div>
}
}

Categories