I am trying to display both of these arrays. Everytime you click the button a new user is generated and I added to the array. I would like for it to render the entire array and not only the newest lines. I have tried many things. I keep seeing online about mapping but I can not get any of that to work correctly.
import React from "react";
import ReactDOM from "react-dom";
import $ from "jquery";
var personlist = [];
var imglist = [];
var i = 0;
class People extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "firstlastname",
image: "image"
};
}
static getDerivedStateFromProps(props, state) {
getUser = () => {
var usersname;
var thumbnail;
$.ajax({
url: "https://randomuser.me/api/?inc=name,picture",
data: "json",
async: false,
success: function (data) {
usersname = JSON.stringify(
data.results[0].name.first + " " + data.results[0].name.last
);
thumbnail = JSON.stringify(data.results[0].picture.thumbnail);
newUser = usersname;
newImage = thumbnail;
var eventstring = new String(); //remove quotes from image string
eventstring = newImage.toString().replace(/"/g, ""); //remove quotes from image string
personlist.push([newUser, eventstring]);
imglist.push([eventstring]);
console.log(personlist);
},
error: function (errormsg) {
console.log(errormsg);
}
});
};
this.getUser();
var eventstring = new String(); //remove quotes from image string
eventstring = newImage.toString().replace(/"/g, ""); //remove quotes from image string
return { name: newUser, image: eventstring };
}
changeColor = () => {
//change state to rerender
this.setState({ name: "updt" });
i++;
return { name: "updt" };
};
render() {
return (
<div>
<button onClick={this.changeColor}>Generate New Person</button>
<div>
<h1>{personlist[i][0]}</h1>
<img src={personlist[i][1]} alt="none" />
</div>
</div>
);
}
}
export default People;
React components can store data in their state variable and in classes, you can access the state using this.state and if you want to modify the state, you can use this.setState function. You can read more on the react site.
Below is the refactored code you provided where I've initialized the people array in the state. And in the fetchPerson method, you can see it after fetching the data, it calls this.setState method to update the current state which will cause the component to re-render. I've replaced the changeColor method to onGetNewPerson which you used to render the component in order to get a new person. In the render method, you can see I am using the map function which is available to arrays, you can read more about this in the Mozilla Javascript documentation. If you don't know about classes, you can check about functional components.
class People extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "firstlastname",
image: "image",
people: [],
};
this.onGetNewPerson = this.onGetNewPerson.bind(this);
}
componentDidMount() {
this.fetchPerson();
}
fetchPerson() {
$.ajax({
url: "https://randomuser.me/api/?inc=name,picture",
data: "json",
async: false,
success: function (data) {
// if you want to replace the people array in the state
// this.setState({ people: data.results });
// if you want to append to the people array
this.setState((prevState) => {
return {
people: prevState.people.concat(data.results)
};
});
}
});
}
parsePerson(person) {
const name = JSON.stringify(
person.name.first + " " + person.name.last
);
const image = JSON.stringify(person.picture.thumbnail).toString().replace(/"/g, "");
return { name, image };
}
onGetNewPerson = () => {
// on click => fetch and append new person
this.fetchPerson();
};
render() {
return (
<div>
<button onClick={this.onGetNewPerson}>Generate New Person</button>
<div>
{this.state.people.map((person, i) => {
const p = this.parsePerson(person);
return (
<div key={"person-" + i}>
<h1>{p.name}</h1>
<img src={p.image} alt="none" />
</div>
);
})}
</div>
</div>
);
}
}
Related
I have a simple word/definition app in React. There is an edit box that pops up to change definition when a user clicks on "edit". The new definition provided is updated in the state when I call getGlossary(), I see the new definition in inspector and a console.log statement in my App render() function triggers too. Unfortunately, I still have to refresh the page in order for the new definition to be seen on screen. I would think that calling set state for this.state.glossary in the App would trigger a re-render down to GlossaryList and then to GlossaryItem to update it's definition but I'm not seeing it :(.
App.js
class App extends React.Component {
constructor() {
super();
this.state = {
glossary: [],
searchTerm: '',
}
this.getGlossary = this.getGlossary.bind(this); //not really necessary?
this.handleSearchChange = this.handleSearchChange.bind(this);
this.handleAddGlossaryItem = this.handleAddGlossaryItem.bind(this);
this.handleDeleteGlossaryItem = this.handleDeleteGlossaryItem.bind(this);
//this.handleUpdateGlossaryDefinition = this.handleUpdateGlossaryDefinition.bind(this);
}
getGlossary = () => {
console.log('getGlossary fired');
axios.get('/words').then((response) => {
const glossary = response.data;
console.log('1: ' + JSON.stringify(this.state.glossary));
this.setState({ glossary }, () => {
console.log('2: ' + JSON.stringify(this.state.glossary));
});
})
}
componentDidMount = () => {
//console.log('mounted')
this.getGlossary();
}
handleSearchChange = (searchTerm) => {
this.setState({ searchTerm });
}
handleAddGlossaryItem = (glossaryItemToAdd) => {
//console.log(glossaryItemToAdd);
axios.post('/words', glossaryItemToAdd).then(() => {
this.getGlossary();
});
}
handleDeleteGlossaryItem = (glossaryItemId) => {
console.log('id to delete: ' + glossaryItemId);
axios.delete('/words', {
data: { glossaryItemId },
}).then(() => {
this.getGlossary();
});
}
render() {
console.log('render app fired');
const filteredGlossary = this.state.glossary.filter((glossaryItem) => {
return glossaryItem.word.toLowerCase().includes(this.state.searchTerm.toLowerCase());
});
return (
<div>
<div className="main-grid-layout">
<div className="form-left">
<SearchBox handleSearchChange={this.handleSearchChange} />
<AddWord handleAddGlossaryItem={this.handleAddGlossaryItem} />
</div>
<GlossaryList
glossary={filteredGlossary}
handleDeleteGlossaryItem={this.handleDeleteGlossaryItem}
getGlossary={this.getGlossary}
//handleUpdateGlossaryDefinition={this.handleUpdateGlossaryDefinition}
/>
</div>
</div>
);
}
}
export default App;
GlossaryItem.jsx
import React from 'react';
import EditWord from './EditWord.jsx';
const axios = require('axios');
class GlossaryItem extends React.Component {
constructor(props) {
super(props);
this.state = {
isInEditMode: false,
}
this.glossaryItem = this.props.glossaryItem;
this.handleDeleteGlossaryItem = this.props.handleDeleteGlossaryItem;
this.handleUpdateGlossaryDefinition = this.handleUpdateGlossaryDefinition.bind(this);
this.handleEditClick = this.handleEditClick.bind(this);
}
handleUpdateGlossaryDefinition = (updateObj) => {
console.log('update object: ' + JSON.stringify(updateObj));
axios.put('/words', {
data: updateObj,
}).then(() => {
this.props.getGlossary();
}).then(() => {
this.setState({ isInEditMode: !this.state.isInEditMode });
//window.location.reload();
});
}
handleEditClick = () => {
// display edit fields
this.setState({ isInEditMode: !this.state.isInEditMode });
// pass const name = new type(arguments); data up to App to handle with db
}
render() {
return (
<div className="glossary-wrapper">
<div className="glossary-item">
<p>{this.glossaryItem.word}</p>
<p>{this.glossaryItem.definition}</p>
<a onClick={this.handleEditClick}>{!this.state.isInEditMode ? 'edit' : 'cancel'}</a>
<a onClick={() => this.handleDeleteGlossaryItem(this.glossaryItem._id)}>delete</a>
</div>
{this.state.isInEditMode ?
<EditWord
id={this.glossaryItem._id}
handleUpdateGlossaryDefinition={this.handleUpdateGlossaryDefinition}
/> : null}
</div>
);
}
}
EditWord
import React from 'react';
class EditWord extends React.Component {
constructor(props) {
super(props);
this.state = {
definition: ''
};
this.handleUpdateGlossaryDefinition = this.props.handleUpdateGlossaryDefinition;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
let definition = event.target.value;
this.setState({ definition });
}
handleSubmit(event) {
//console.log(event.target[0].value);
let definition = event.target[0].value;
let update = {
'id': this.props.id,
'definition': definition,
}
//console.log(update);
this.handleUpdateGlossaryDefinition(update);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit} className="glossary-item">
<div></div>
<input type="text" name="definition" placeholder='New definition' value={this.state.definition} onChange={this.handleChange} />
<input type="submit" name="update" value="Update" />
</form>
);
}
}
export default EditWord;
Thank you
One possible way I can see to fix this is to map the data to make the id uniquely identify each list item (even in case of update). We can to do this in getGlossary() by modifying the _id to _id + definition.
getGlossary = () => {
console.log('getGlossary fired');
axios.get('/words').then((response) => {
// Map glossary to uniquely identify each list item
const glossary = response.data.map(d => {
return {
...d,
_id: d._id + d.definition,
}
});
console.log('1: ' + JSON.stringify(this.state.glossary));
this.setState({ glossary }, () => {
console.log('2: ' + JSON.stringify(this.state.glossary));
});
})
}
In the constructor of GlossaryItem I set
this.glossaryItem = this.props.glossaryItem;
because I am lazy and didn't want to have to write the word 'props' in the component. Turns out this made react loose reference somehow.
If I just remove this line of code and change all references to this.glossaryItem.xxx to this.pros.glossaryItem.xxx then it works as I expect! On another note, the line of code can be moved into the render function (instead of the constructor) and that works too, but have to make sure I'm accessing variables properly in the other functions outside render.
The remove() function is called from an object. How can I get updated state value inside of that remove() function.
const [InfoBoxPin, setInfoBoxPin] = useState([])
const createInfoBoxPin = (descriptions) =>{
var newPin = {
"location":currentLoc,
"addHandler":"mouseover",
"infoboxOption": {
title: 'Comment',
description: "No comment Added",
actions: [{
label:'Remove Pin',
eventHandler: function () {
remove(newPin.location) //FUNCTION CALLED HERE
}
}] }
}
setInfoBoxPin((InfoBoxPin)=>[...InfoBoxPin, newPin ]) // UPDATE STATE. Push the above object.
}
const remove = (pos) =>{
console.log(InfoBoxPin) //NEVER GETTING UPDATED STATE HERE.
//Other codes here......
}
This is a bing map Info card. Eventhandler creates a button which can call any function.
The problem is that you are referring to old state information in the remove function.
When you call setInfoBoxPin - the state of InfoBoxPin is registered for an update on the next render of UI. This means that in current state it will be the same (empty) and all links to it will refer to an empty array.
In order to fix this, you will have to pass your new state to appropriate functions from the View itself.
Example #1
Here, I have created a CodeSandBox for you:
https://codesandbox.io/s/react-setstate-example-4d5eg?file=/src/App.js
And here is the code snipped from it:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [state, setState] = useState({
InfoBoxPin: [],
output: []
});
const createInfoBoxPin = (descriptions) => {
var newPin = {
location: Math.round(Math.random(10) * 1000),
addHandler: "mouseover",
infoboxOption: {
title: "Comment",
description: "No comment Added",
actions: [
{
label: "Remove Pin",
eventHandler: removePin
},
{
label: "Update Pin",
eventHandler: updatePin
}
]
}
};
setState({ ...state, InfoBoxPin: [...state.InfoBoxPin, newPin] });
};
const updatePin = (key, state) => {
var text = `Updating pin with key #${key} - ${state.InfoBoxPin[key].location}`;
setState({ ...state, output: [...state.output, text] });
console.log(text, state.InfoBoxPin);
};
const removePin = (key, state) => {
var text = `Removing pin with key #${key} - ${state.InfoBoxPin[key].location}`;
setState({ ...state, output: [...state.output, text] });
console.log(text, state.InfoBoxPin);
};
return (
<div className="App">
<h1>React setState Example</h1>
<h2>Click on a button to add new Pin</h2>
<button onClick={createInfoBoxPin}>Add new Pin</button>
<div>----</div>
{state.InfoBoxPin.map((pin, pin_key) => {
return (
<div key={pin_key}>
<span>Pin: {pin.location} </span>
{pin.infoboxOption.actions.map((action, action_key) => {
return (
<button
key={action_key}
onClick={() => action.eventHandler(pin_key, state)}
>
{action.label}
</button>
);
})}
</div>
);
})}
<h4> OUTPUT </h4>
<ul style={{ textAlign: "left" }}>
{state.output.map((txt, i) => {
return <li key={i}>{txt}</li>;
})}
</ul>
</div>
);
}
As you can see I am providing a new state with InfoBoxPin value to a function named eventHandler for onclick event listener of a button.
And then in that function, I can use the new InfoBoxPin value from state how I need it.
Example #2 (ES6)
In this example, I am using a bit different structure for App - using class (ES6)
By using a class for App, we can manipulate App state using different methods.
func.bind(this) can be used on defined function on initialization
func.call(this) can be used to call a dynamic function without arguments
func.apply(this, [args]) can be used to call a dynamic function with arguments
CodeSandBox Link:
https://codesandbox.io/s/react-setstate-example-using-class-cz2u4?file=/src/App.js
Code Snippet:
import React from "react";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
InfoBoxPin: [],
pinName: ""
};
/* ------------ method #1 using .bind(this) ------------ */
this.setPinName = this.setPinName.bind(this);
}
remove(key) {
this.state.InfoBoxPin.splice(key, 1);
this.setState({ InfoBoxPin: this.state.InfoBoxPin });
}
add(pinName) {
this.state.InfoBoxPin.push(pinName);
this.setState({ InfoBoxPin: this.state.InfoBoxPin });
}
processPinNameAndAdd() {
let pinName = this.state.pinName.trim();
if (pinName === "") pinName = Math.round(Math.random() * 1000);
this.add(pinName);
}
setPinName(event) {
this.setState({ pinName: event.target.value });
}
render() {
return (
<div className="shopping-list">
<h1>Pin List</h1>
<p>Hit "Add New Pin" button.</p>
<p>(Optional) Provide your own name for the pin</p>
<input
onInput={this.setPinName}
value={this.state.pinName}
placeholder="Custom name"
></input>
{/* ------------ method #2 using .call(this) ------------ */}
<button onClick={() => this.processPinNameAndAdd.call(this)}>
Add new Pin
</button>
<ul>
{this.state.InfoBoxPin.map((pin, pinKey) => {
return (
<li key={pinKey}>
<div>pin: {pin}</div>
{/* ------------ method #3 using .apply(this, [args]) ------------ */}
<button onClick={() => this.remove.apply(this, [pinKey])}>
Delete Pin
</button>
</li>
);
})}
</ul>
</div>
);
}
}
export default App;
Example #3 (ES6) without access to the created element
This example will show how to handle callbacks from third-party libraries with our own arguments and state data from the event of an auto-generated HTML element
CodeSandBox Link:
https://codesandbox.io/s/react-setstate-example-using-class-no-element-control-lcz5d?file=/src/App.js
Code Snippet:
import React from "react";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
InfoBoxPin: [],
lastPinId: 0,
pinName: ""
};
this.setPinName = this.setPinName.bind(this);
}
remove(id) {
let keyToRemove = null;
this.state.InfoBoxPin.forEach((pin, key) => {
if (pin.id === id) keyToRemove = key;
});
this.state.InfoBoxPin.splice(keyToRemove, 1);
this.setState({ InfoBoxPin: this.state.InfoBoxPin });
}
add(data, id) {
this.state.InfoBoxPin.push({ id: id, data: data });
this.setState({
InfoBoxPin: this.state.InfoBoxPin,
lastPinId: id
});
}
processPinNameAndAdd() {
let pinName = this.state.pinName.trim();
if (pinName === "") pinName = Math.round(Math.random() * 1000);
var newPinId = this.state.lastPinId + 1;
var newPin = {
location: pinName,
addHandler: "mouseover",
infoboxOption: {
title: "Comment",
description: "No comment Added",
actions: [
{
label: "Remove Pin #" + newPinId,
// [ES6 class only] using () => func() for callback function
// By doing so we don't need to use bind,call,apply to pass class ref [this] to a function.
eventHandler: () => this.remove(newPinId)
}
]
}
};
this.add(newPin, newPinId);
}
setPinName(event) {
this.setState({ pinName: event.target.value });
}
render() {
return (
<div className="shopping-list">
<h1>Pin List</h1>
<p>Hit "Add New Pin" button.</p>
<p>(Optional) Provide your own name for the pin</p>
<input onInput={this.setPinName} value={this.state.pinName}></input>
{/*
[ES6 class only] Using {() => func()} for event handler.
By doing so we don't need to use func.bind(this) for passing class ref at constructor
*/}
<button onClick={() => this.processPinNameAndAdd()}>Add new Pin</button>
<ul>
{this.state.InfoBoxPin.map((pin, pKey) => {
return (
<li key={pKey}>
<div>pin: {pin.data.location}</div>
{pin.data.infoboxOption.actions.map((action, aKey) => {
return (
<button key={aKey} onClick={action.eventHandler}>
{action.label}
</button>
);
})}
</li>
);
})}
</ul>
</div>
);
}
}
export default App;
I have created lastPinId entry in a State to track newly created Pin's ids.
Pin id can be used later to find the desired pin in the InfoBoxPin collection for removal.
The most important part how to register your eventHandler is this:
eventHandler: () => this.remove(newPinId)
Please note that using arrow function () => func is important to pass class ref to remove function.
I've got a nested object that somehow retains a linkage to a profile object. Every time I call myMethod and makes changes to userObj, these changes are reflected in all elements of the nested object. For example, allProfiles['a'] and allProfiles['b'] have the same values for allProfiles[][].userObj properties. This only happens with the userObj data, everything is works as expected. The following snippet duplicates the issue.
import React from 'react';
import { ReactDOM, render } from 'react-dom';
export const userProfile = {
address1: { label: "Local", data: null },
address2: { label: "World", data: null },
};
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
profiles: {}
};
this.addProfile = this.addProfile.bind(this);
}
addProfile() {
const { counter, profiles } = this.state;
let new_user = Object.assign({}, userProfile);
new_user.address1.data = counter * 5;
const profile_id = 1;
const user_id = counter;
const new_profile = {};
const show = true;
new_profile[profile_id] = { show };
new_profile[profile_id] = new_user;
profiles[user_id] = new_profile;
this.setState({ counter: user_id + 1, profiles });
}
render() {
const profile_id = 1;
const ctr = this.state.counter;
return (
<div>
<div><button onClick={this.addProfile}>add profile</button></div>
<div>profile 0 data:
{ctr > 0 ? this.state.profiles[0][profile_id].address1.data : null}</div>
<div>profile {ctr} data:
{ctr > 0 ? this.state.profiles[ctr - 1][profile_id].address1.data : null}</div>
</div>
);
}
}
export default (Toggle);
render(<Toggle />, document.getElementById('root'));
Object.assign({}, userProfile) only creates a shallow copy of userProfile, meaning new_user.address1 and userProfile.address1 are still referencing the same object.
To properly clone the address1 and address2 objects, you need to do
const new_user = Object.entries(userProfile).reduce((acc, [key, value]) => {
acc[key] = { ...value }; // create a shallow copy of the nested object
return acc;
}, {});
This should fix your problem but there's a lot of weird things going on in your code, like assigning new_profile[profile_id] consecutively.
I have a simple search bar which uses a react-autosuggest. When I create a suggestion, I want to attach an onClick handler. This onClick has been passed down from a parent class. When the suggestion is rendered however, this is undefined and therefore the click handler is not attached.
I have attached the component below, the logic which is not working is in the renderSuggestion method.
import Autosuggest from 'react-autosuggest'
import React from 'react'
export class SearchBar extends React.Component {
static getSuggestionValue(suggestion) {
return suggestion;
}
static escapeRegexCharacters(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
listOfValues: this.props.tickers
};
}
onChange = (event, { newValue, method }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: this.getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
renderSuggestion(suggestion) {
return (
<span onClick={() => this.props.clickHandler(suggestion)}>{suggestion}</span>
);
}
getSuggestions(value) {
const escapedValue = SearchBar.escapeRegexCharacters(value.trim());
if (escapedValue === '') {
return [];
}
const regex = new RegExp('^' + escapedValue, 'i');
return this.state.listOfValues.filter(ticker => regex.test(ticker));
}
render() {
const { value, suggestions } = this.state;
const inputProps = {
placeholder: "Search for stocks...",
value,
onChange: this.onChange
};
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={SearchBar.getSuggestionValue}
renderSuggestion={this.renderSuggestion}
inputProps={inputProps} />
);
}
}
This is becuase you need to bind "this" to your function.
If you add this code to your constructor
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
listOfValues: this.props.tickers
};
//this line of code binds this to your function so you can use it
this.renderSuggestion = this.renderSuggestion.bind(this);
}
It should work. More info can be found at https://reactjs.org/docs/handling-events.html
In the scope of renderSuggestion, this isn't referring to the instance of the class.
Turning renderSuggestion into an arrow function like you've done elsewhere will ensure that this refers to the instance of the class.
renderSuggestion = (suggestion) => {
return (
<span onClick={() => this.props.clickHandler(suggestion)}>{suggestion}</span>
);
}
The AJAX call returns data but how do I assign it to a class variable so that I can use it in another method? Here's my code(this is reactjs component):
import React from 'react';
import jQuery from 'jquery';
import ListPotions from './list_potions';
export default class Potions extends React.Component {
constructor(props) {
super(props);
this.state = {potions: []};
this.fetchPotions = this.fetchPotions.bind(this);
this.objToStrMap = this.objToStrMap.bind(this);
}
componentDidMount() {
this.fetchPotions();
}
fetchPotions() {
jQuery.ajax({
url: this.props.endPoint,
dataType: 'json',
cache: false,
headers: {
"Authorization": btoa('mixOfRandomPotions')
},
success: function(data) {
let potions = this.objToStrMap(data);
this.setState({ potions });
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.endPoint, status,
err.toString());
}.bind(this)
});
}
objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
render(){
console.log(this.potions);
return (
<div className="{this.props.className}">
<ul>
{this.state.potions.map((potion) => <ListPotions key="{potion.id}" potion={potion} /> )}
</ul>
</div>
);
}
}
As you can see I'm assigning it to this.potions but in render() method, the list is empty.
this.potions is probably updated, but your component is not being re-rendered with the new data. In React, you can use state and setState to easily update the internal data of a component. Here's your code (simplified):
class Potions extends React.Component {
constructor(props) {
super(props);
this.state = { potions: [] };
this.fetchPotions = this.fetchPotions.bind(this);
}
componentDidMount() {
this.fetchPotions();
}
fetchPotions() {
// not a network request, I just set some sample data. your request would go here.
const potions = [{ id: 1, name: 'first' }, { id: 2, name: 'second' }];
// instead of overwriting a variable (e.g. this.potions), we update the state
// put this into your network request callback!
this.setState({ potions });
}
render() {
console.log(this.state.potions);
return (
<div className="{this.props.className}">
<ul>
{this.state.potions.map((potion) => <li key={potion.id}>{potion.name}</li> )}
</ul>
</div>
);
}
}
ReactDOM.render(<Potions/>, document.getElementById('View'));
<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="View"></div>
You should handle the case when the data is empty
render() {
const { potions, className } = this.state;
if (!potions) {
return (<p>Loading...</p>);
} else {
return (
<div className="{className}">
<ul>
{potions.map(potion => <li key={potion.id}>{potion.name}</li> )}
</ul>
</div>
);
}
}