How to change DOM using onSubmit in React JS? - javascript

I am using react for the front end of a search application.
When user submits a query and a list of results pop up, each with a button that says "Learn More". When the "Learn More" button is pressed, the list of results should all disappear and be replaced with the information on that topic that was selected.
The search bar above should stay in place, if a user searches new information, the learn more info should go away and the new list of results should appear.
I am having trouble displaying the learn more information.
The biggest issue I am having is that I have to use the form with the onSubmit function and as soon as the onSubmit function is called my results will stay for a few seconds and then everything will disappear.
The following shows the parts of my file related to the issue
class Search extends React.Component {
learnMore(obj){
//Here is where i would like to replace the results class with the learn more info. obj.learnMore has the info stored
}
render() {
return (
<div className="search">
<div className="search-bar">
// Here is where my search bar is, results of search get added to results array
</div>
<div className= "results">
{this.state.results.map((obj) =>
<div key={obj.id}>
<p> {obj.name} </p>
<form id= "learn-more-form" onSubmit={() => {this.learnMore(obj); return false;}}>
<input type="submit" value="Learn More"/>
</form>
</div>
)}
</div>
</div>
);
}
}

There are many ways to handle this scenario. In this case, I recommend separating containers from components. The container will handle all things state and update its children components accordingly.
Please note that this example uses a lot of ES6 syntaxes. Please read the following to understand how some of it works: fat arrow functions, ES6 destruction, spread operator, ternary operator, class properties, a controlled react form utilizing event handlers and state, array filtering, and type checking with PropTypes.
It's a lot to take in, so if you have any questions, feel free to ask.
Working example:
containers/SeachForm
import React, { Component } from "react";
import moment from "moment";
import LearnMore from "../../components/LearnMore";
import Results from "../../components/Results";
import SearchBar from "../../components/Searchbar";
const data = [
{
id: "1",
name: "Bob",
age: 32,
email: "bob#example.com",
registered: moment("20111031", "YYYYMMDD").fromNow(),
description: "Bob is a stay at home dad."
},
{
id: "2",
name: "Jane",
age: 43,
email: "jane#example.com",
registered: moment("20010810", "YYYYMMDD").fromNow(),
description: "Jane is a CEO at Oracle."
},
{
id: "3",
name: "Yusef",
age: 21,
email: "yusef#example.com",
registered: moment("20180421", "YYYYMMDD").fromNow(),
description: "Yusef is a student at UC Berkeley."
},
{
id: "4",
name: "Dasha",
age: 29,
email: "dasha#example.com",
registered: moment("20050102", "YYYYMMDD").fromNow(),
description: "Dasha is an owner of a local antique shop."
},
{
id: "5",
name: "Polina",
age: 18,
email: "dasha#example.com",
registered: moment("20190102", "YYYYMMDD").fromNow(),
description: "Polina works at a local movie theather."
}
];
const initialState = {
searchQuery: "",
results: data, // <== change this to an empty array if you don't want to show initial user data
learnMore: false
};
class SearchForm extends Component {
state = { ...initialState }; // spreading out the initialState object defined above; it'll be the same as: "state = { searchQuery: "", results: data, learnMore: false }; "
handleSubmit = e => {
e.preventDefault(); // prevents a page refresh
if (!this.state.searchQuery) return null; // prevents empty search submissions
this.setState({
results: data.filter(
person => person.name.toLowerCase() === this.state.searchQuery.toLowerCase()
) // filters the dataset with the "searchQuery" (lowercased names) and returns the result if it finds a match
});
};
handleSearch = ({ target: { value } }) =>
this.setState({ searchQuery: value }); // updates searchQuery input with an event.target.value
handleReset = () => this.setState({ ...initialState }); // resets to initial state
handleLearnMore = person => {
this.setState({ learnMore: true, results: person }); // sets learnMore to true (to show the "LearnMore" component) and sets results to the selected user
};
render = () => (
<div className="container">
<SearchBar
handleReset={this.handleReset}
handleSearch={this.handleSearch}
handleSubmit={this.handleSubmit}
searchQuery={this.state.searchQuery}
/>
{!this.state.learnMore ? ( // if learnMore is false, then show "Results"
<Results
results={this.state.results}
handleLearnMore={this.handleLearnMore}
/>
) : (
<LearnMore {...this.state.results} /> // otherwise, show LearnMore
)}
</div>
);
}
export default SearchForm;
components/SearchBar
import React from "react";
import PropTypes from "prop-types";
const SearchBar = ({
handleReset,
handleSearch,
handleSubmit,
searchQuery
}) => (
<div className="search">
<div className="search-bar">
<form onSubmit={handleSubmit}>
<input
type="text"
className="uk-input"
value={searchQuery}
placeholder="Search for a name"
onChange={handleSearch}
/>
<div className="button-container">
<button
type="button"
className="uk-button uk-button-danger reset"
onClick={handleReset}
>
Reset
</button>
<button type="submit" className="uk-button uk-button-primary submit">
Submit
</button>
</div>
</form>
</div>
</div>
);
SearchBar.propTypes = {
handleReset: PropTypes.func.isRequired,
handleSearch: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired,
searchQuery: PropTypes.string
};
export default SearchBar;
components/Results
import React from "react";
import PropTypes from "prop-types";
const Results = ({ handleLearnMore, results }) => (
<div className="results">
{results && results.length > 0 ? (
results.map(person => (
<div key={person.id} className="uk-card uk-card-default uk-width-1-2#m">
<div className="uk-card-header">
<div className="uk-width-expand">
<h3 className="uk-card-title uk-margin-remove-bottom">
{person.name}
</h3>
</div>
</div>
<div className="uk-card-body">
<p>{person.description}</p>
</div>
<div className="uk-card-footer">
<button
onClick={() => handleLearnMore(person)}
className="uk-button uk-button-text"
>
Learn More
</button>
</div>
</div>
))
) : (
<div className="uk-placeholder">No users were found!</div>
)}
</div>
);
Results.propTypes = {
handleLearnMore: PropTypes.func.isRequired,
results: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
age: PropTypes.number,
email: PropTypes.string,
registered: PropTypes.string,
description: PropTypes.string
})
)
};
export default Results;
components/LearnMore
import React from "react";
import PropTypes from "prop-types";
const LearnMore = ({ name, email, age, description, registered }) => (
<div className="uk-card uk-card-default uk-card-body">
<h3 className="uk-card-header">{name}</h3>
<p>
<strong>Email</strong>: {email}
</p>
<p>
<strong>Registered</strong>: {registered}
</p>
<p>
<strong>Age</strong>: {age}
</p>
<p>
<strong>Job</strong>: {description}
</p>
</div>
);
LearnMore.propTypes = {
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
registered: PropTypes.string.isRequired,
description: PropTypes.string.isRequired
};
export default LearnMore;

You should do your onSubmit like this:
<form id= "learn-more-form" onSubmit={this.learnMore(obj)}>
<input type="submit" value="Learn More"/>
</form>
Then the function should be:
learnMore = (data) => (e) => {
e.preventDefault()
console.log(data) // probably setState with this data so you can display it when it, like this.setState({ currentMoreResults: data })
}

Related

Property 'map' of undefined in React

I'm learning a react course online. When I try to display the list of items from an array using map to display in a child component , I keep getting "cannot read property map of undefined.
Error is thrown while fetching data from users
import React, { Component } from "react";
import ReactDOM from "react-dom";
let userList = [
{ name: "John", age: 24, place: "India" },
{ name: "Henry", age: 24, place: "India" },
{ name: "Ulrich", age: 24, place: "India" }
];
const AppChild = ({ name, age, place, Graduated }) => {
return (
<section>
<p>name: {name}</p>
<p>age: {age}</p>
<p>place: {place}</p>
{/* access the value via props */}
<p>Graduated: {Graduated ? "yes!" : "no!"}</p>
</section>
);
};
export default class App extends Component {
state = {
userExists: true,
isGraduated: true,
loading: true,
};
toggleStatus = () => {
this.setState(prevState => ({
userExists: !prevState.userExists // value : false
}));
};
render() {
const { users } = this.props;
return (
<div>
<h2>Profile</h2>
<h4>
Profile Status is {this.state.userExists ? "Updated" : "Outdated"}
<br />
<button onClick={this.toggleStatus}>Check Status</button>
</h4>
{users.map(user => (
<AppChild
name={user.name}
age={user.age}
place={user.place}
Graduated={this.state.isGraduated} // passing state to child component
/>
))}
</div>
);
}
}
ReactDOM.render(<App users={userList} />, document.getElementById("root"));
To figure out the problem, we follow the bouncing ball. From the error message, I guess that the problem occurs on the line
{users.map(user => (
(You can confirm this from the stack trace given with the error message.)
The error tells you that users is undefined. So we look at the declaration for users:
const { users } = this.props;
Ok, so it is really this.props.users. So we look where this is passed in:
ReactDOM.render(<App users={userList} />, document.getElementById("root"));
Here you are passing the value of userList to a prop named users. However, in the code you show here, there is no variable named userList. This is as far as we can go with the information you have given. You need to find where this variable is declared and initialized to continue solving the problem.
Below is the correct code. In the previous code I was trying to render <App/> in both index.js and App.js. Thanks everyone for helping me out
=>index.js
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
let userList = [
{ name: "John", age: 24, place: "India" },
{ name: "Henry", age: 24, place: "India" },
{ name: "Ulrich", age: 24, place: "India" }
];
ReactDOM.render(<App users={userList} />, document.getElementById("root"));
=> App.js
import React, { Component } from "react";
// child component
const AppChild = ({ name, age, place, Graduated }) => {
return (
<section>
<p>name: {name}</p>
<p>age: {age}</p>
<p>place: {place}</p>
{/* access the value via props */}
<p>Graduated: {Graduated ? "yes!" : "no!"}</p>
</section>
);
};
// parent component
export default class App extends Component {
state = {
userExists: true,
isGraduated: true,
loading: true,
};
toggleStatus = () => {
this.setState(prevState => ({
userExists: !prevState.userExists // value : false
}));
};
render() {
const { users } = this.props;
return (
<div>
<h2>Profile</h2>
<h4>
Profile Status is {this.state.userExists ? "Updated" : "Outdated"}
<br />
<button onClick={this.toggleStatus}>Check Status</button>
</h4>
{users.map((user) => {
return(
<AppChild
name={user.name}
age={user.age}
place={user.place}
Graduated={this.state.isGraduated} // passing state to child component
/>
)})}
</div>
);
}
}
If you try to log users after following line of code
const { users } = this.props;
you'll see users is undefined.
Error message "cannot read property map of undefined" says the same thing, you can not apply map helper on an undefined variable. The map works with arrays

Creating HTML tag in react component

I'm not extending component class, trying to use usestate to manage state. Now I want to add a person component on certain conditions to personList variable inside the method togglePersonsHanler.
I'm expecting a list of HTML tags to be added like
<person name="person1" age=31>
<person name="person2" age=26>
<person name="person3" age=35>
but on console log, I'm getting personList as below
{$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}$$typeof: Symbol(react.element)type: "div"key: nullref: nullprops: {children: Array(3)}_owner: null_store: {validated: false}_self: null_source: {fileName: "D:\data\react\my-app\src\App.js", lineNumber: 72, columnNumber: 7}
and person tag is not getting added to DOM, any advice, please
import React, { useState } from 'react';
import './App.css';
import Person from './Person/Person';
const App = props => {
const [personState, setPersonState] = useState({
persons: [
{name: "person1", age:31},
{name: "person2", age:26},
{name: "person3", age:25}
],
other: "some Other Value"
} );
const [otherState,setOtherState]=useState({otherState :'some other value'});
const [showPersonsState,setShowPersonsState]=useState({showPersons :false});
let personList=null;
const togglePersonsHanler =() =>{
personList=null;
setShowPersonsState(
{showPersons : !showPersonsState.showPersons}
)
console.log(showPersonsState.showPersons);
if(showPersonsState.showPersons){
personList=(
<div>{personState.persons.map (person =>{
return <person name={person.name} age={person.age}/>
}
)}</div>
);
}
console.log(personList);
}
return (
<div className="App">
<h1> HI, I'm the react app</h1>
<button
//onClick={switchNameHandler.bind(this,'Gopu Ravi')}
onClick={togglePersonsHanler}
style={style}> Toggle Person </button>
{ personList }
</div>
);
}
export default App;
You're mapping the object literals by using them as an html tag. You likely meant to use the imported Person component.
<div>
{personState.persons.map (person => (
<Person name={person.name} age={person.age}/>
)}
</div>
And to fix a react-key warning since all mapped elements need unique keys, add a key prop with a value that is unique to the data in the array, like name:
<div>
{personState.persons.map (person => (
<Person key={name} name={person.name} age={person.age}/>
)}
</div>
To correctly toggle the display of the "personList":
Conditionally render the mapped persons array if showPersonsState is true
Simplify showPersonsState state to simply be the boolean value
Use functional state update to correctly toggle showPersonsState from previous state
Updated component code
const App = props => {
const [personState, setPersonState] = useState({
persons: [
{ name: "person1", age: 31 },
{ name: "person2", age: 26 },
{ name: "person3", age: 25 }
],
other: "some Other Value"
});
const [otherState, setOtherState] = useState({
otherState: "some other value"
});
const [showPersonsState, setShowPersonsState] = useState(false);
const togglePersonsHandler = () => setShowPersonsState(show => !show);
return (
<div className="App">
<h1> HI, I'm the react app</h1>
<button onClick={togglePersonsHandler}>Toggle Person</button>
{showPersonsState &&
personState.persons.map(({ age, name }) => (
<Person key={`${name}`} name={name} age={age} />
))}
</div>
);
};

Proper way to update state in nested component

I am using a few different technologies in a self learning project (reddit clone):
react
react-redux
redux
react-router (v4)
Goal: When clicking on an edit link to a post, go to proper route with edit form fields populated.
I am using a container that dispatches an action and re-renders updating the component inside the containers props. It looks like this:
CONTAINER:
class PostContainer extends Component {
componentDidMount() {
debugger;
const {id} = this.props.match.params;
this.props.dispatch(fetchPost(id));
}
render() {
return (
<Post post={this.props.post} editing={this.props.location.state? this.props.location.state.editing : false}/>
);
}
}
const mapStateToProps = (state, ownProps) => {
debugger;
return {
...ownProps,
post: state.post
};
};
export default connect(mapStateToProps)(withRouter(PostContainer));
The nested Post component has additional nested components:
POST Component:
class Post extends Component {
constructor(props) {
super(props);
this.editToggle = this.editToggle.bind(this);
}
state = {
editing: this.props.location.state ? this.props.location.state.editing : false
};
editToggle() {
this.setState({editing: !this.state.editing});
}
render() {
const {editing} = this.state;
if (editing || this.props.post === undefined) {
return <PostEditForm editToggle={this.editToggle} editing={this.props.editing} post={this.props.post || {}}/>;
}
return (
<div>
<div>
<h2>Title: {this.props.post.title}</h2>
<span>Author: {this.props.post.author}</span>
<br/>
<span>Posted: {distanceInWordsToNow(this.props.post.timestamp)} ago f</span>
<p>Body: {this.props.post.body}</p>
<button type='button' className='btn btn-primary' onClick={() => this.editToggle()}>Make Edit</button>
</div>
<hr/>
<Comments post={this.props.post}></Comments>
</div>
);
}
}
export default withRouter(Post);
In the render function within the first if statement, I pass the updated props to <PostEditForm>. When PostEditForm receives the props, it re-renders the component, but the state of the component is not updated.
PostEditForm:
class PostEditForm extends Component {
state = {
timestamp: this.props.post.timestamp || Date.now(),
editing: this.props.editing || false,
body: this.props.post.body || '',
title: this.props.post.title || '',
category: this.props.post.category || '',
author: this.props.post.author || '',
id: this.props.post.id || uuid()
};
clearFormInfo = () => {
this.setState({
timestamp: Date.now(),
body: '',
title: '',
category: '',
author: '',
id: uuid(),
editing: false
});
};
handleOnChange = (e) => {
this.setState({[e.target.id]: e.target.value});
};
handleSubmit = event => {
event.preventDefault();
const post = {
...this.state
};
if(this.state.editing) {
this.props.dispatch(updatePostAPI(post));
this.setState({
editing: false
})
} else {
this.props.dispatch(createPostAPI(post));
this.clearFormInfo();
window.location.href = `/${post.category}/${post.id}`;
}
};
render() {
const {editing} = this.state;
return (
<form onSubmit={this.handleSubmit}>
<div>
<h2>Create New Post</h2>
<label htmlFor='text'>
Title:
<input
type="text"
onChange={this.handleOnChange}
value={this.state.title}
placeholder="Enter Title"
required
id="title"
name="title"
/>
</label>
<label>
Author:
<input
type="text"
onChange={this.handleOnChange}
value={this.state.author}
placeholder="Enter Author"
required
id="author"
name="author"
/>
</label>
<label>
</label>
<label>
Body:
<textarea
type="text"
onChange={this.handleOnChange}
value={this.state.body}
placeholder="Enter Body"
required
id="body"
name="body"
/>
</label>
</div>
<label>
Category:
<select id = 'category' onChange={this.handleOnChange} value={this.state.value} required>
<option value='Select Category'>Select Category</option>
<option value='react'>react</option>
<option value='redux'>redux</option>
<option value='udacity'>udacity</option>
</select>
</label>
<button type='submit'>Create Post</button>
</form>
);
}
}
export default connect()(PostEditForm);
I believe I need to call setState and assign the new props passed into the state, but I don't know which lifeCycle method to use.
When I use componentDidUpdate, something like:
class PostEditForm extends Component {
componentDidUpdate(prevProps) {
if(this.props.post.id !== prevProps.post.id) {
this.setState({
timestamp: this.props.post.timestamp,
editing: this.props.post.editing,
title: this.props.post.title,
category: this.props.post.category,
author: this.props.post.author,
id: this.props.post.id
})
}
}
.....
componentDidUpdate solves the initial problem, but whenever I update content in the form I am editing, the lifeclycle method is called, eliminating the need for the onChange handlers, is this a good practice or should I use a different approach?
just a note
componentDidUdpate is called after every re-render,
componentDidMount is called after the instantiation of the component.
if you modify your state inside the componentDidUdpate() hook you will end up in an infinite loop since a setState() will retrigger the render() and consequently the componentDidUpdate()
if you modify your state in the componentDidMount() this hook is called only once, after instantiation, not every re-render thus it won't work as well for subsequent updates.
However the problem lays here:
state = {
timestamp: this.props.post.timestamp || Date.now(),
editing: this.props.editing || false,
body: this.props.post.body || '',
title: this.props.post.title || '',
category: this.props.post.category || '',
author: this.props.post.author || '',
id: this.props.post.id || uuid()
};
render() {
const {editing} = this.state;
//change to const {editing} = this.props;
....
}
the way you're declaring the state won't work for future updates, state will point to the values of the props once the instantion is done, just primitive values that are immutable for definition.
you should just refer to this.props across the whole PostEditForm render() method instead of this.state and you should be fine.
to update the state you should pass a callback to PostEditForm from the parent (in this case Post component) and trigger it when needed.
And regarding the default values i'd suggest the use of Proptypes library.
something like:
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class PostEditForm extends Component {
//your business logic
}
PostEditForm.propTypes = {
timestamp: Proptypes.Object,
editing: PropTypes.bool,
body: PropTypes.string,
title: PropTypes.string,
category: PropTypes.string,
author: PropTypes.string,
id: PropTypes.string
}
PostEditForm.defaultProps = {
timestamp: Date.now(),
editing: false,
body: '',
title: '',
category: '',
author: '',
id: uuid()
}

How to render an array of objects in React?

could you please tell me how to render a list in react js.
I do like this
https://plnkr.co/edit/X9Ov5roJtTSk9YhqYUdp?p=preview
class First extends React.Component {
constructor (props){
super(props);
}
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>;
return (
<div>
hello
</div>
);
}
}
You can do it in two ways:
First:
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>);
return (
<div>
{listItems }
</div>
);
}
Second: Directly write the map function in the return
render() {
const data =[{"name":"test1"},{"name":"test2"}];
return (
<div>
{data.map(function(d, idx){
return (<li key={idx}>{d.name}</li>)
})}
</div>
);
}
https://facebook.github.io/react/docs/jsx-in-depth.html#javascript-expressions
You can pass any JavaScript expression as children, by enclosing it within {}. For example, these expressions are equivalent:
<MyComponent>foo</MyComponent>
<MyComponent>{'foo'}</MyComponent>
This is often useful for rendering a list of JSX expressions of arbitrary length. For example, this renders an HTML list:
function Item(props) {
return <li>{props.message}</li>;
}
function TodoList() {
const todos = ['finish doc', 'submit pr', 'nag dan to review'];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}
class First extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [{name: 'bob'}, {name: 'chris'}],
};
}
render() {
return (
<ul>
{this.state.data.map(d => <li key={d.name}>{d.name}</li>)}
</ul>
);
}
}
ReactDOM.render(
<First />,
document.getElementById('root')
);
<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>
Shubham's answer explains very well. This answer is addition to it as per to avoid some pitfalls and refactoring to a more readable syntax
Pitfall : There is common misconception in rendering array of objects especially if there is an update or delete action performed on data. Use case would be like deleting an item from table row. Sometimes when row which is expected to be deleted, does not get deleted and instead other row gets deleted.
To avoid this, use key prop in root element which is looped over in JSX tree of .map(). Also adding React's Fragment will avoid adding another element in between of ul and li when rendered via calling method.
state = {
userData: [
{ id: '1', name: 'Joe', user_type: 'Developer' },
{ id: '2', name: 'Hill', user_type: 'Designer' }
]
};
deleteUser = id => {
// delete operation to remove item
};
renderItems = () => {
const data = this.state.userData;
const mapRows = data.map((item, index) => (
<Fragment key={item.id}>
<li>
{/* Passing unique value to 'key' prop, eases process for virtual DOM to remove specific element and update HTML tree */}
<span>Name : {item.name}</span>
<span>User Type: {item.user_type}</span>
<button onClick={() => this.deleteUser(item.id)}>
Delete User
</button>
</li>
</Fragment>
));
return mapRows;
};
render() {
return <ul>{this.renderItems()}</ul>;
}
Important : Decision to use which value should we pass to key prop also matters as common way is to use index parameter provided by .map().
TLDR; But there's a drawback to it and avoid it as much as possible and use any unique id from data which is being iterated such as item.id. There's a good article on this - https://medium.com/#robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
Try this below code in app.js file, easy to understand
function List({}) {
var nameList = [
{ id: "01", firstname: "Rahul", lastname: "Gulati" },
{ id: "02", firstname: "Ronak", lastname: "Gupta" },
{ id: "03", firstname: "Vaishali", lastname: "Kohli" },
{ id: "04", firstname: "Peter", lastname: "Sharma" }
];
const itemList = nameList.map((item) => (
<li>
{item.firstname} {item.lastname}
</li>
));
return (
<div>
<ol style={{ listStyleType: "none" }}>{itemList}</ol>
</div>
);
}
export default function App() {
return (
<div className="App">
<List />
</div>
);
}
import React from 'react';
class RentalHome extends React.Component{
constructor(){
super();
this.state = {
rentals:[{
_id: 1,
title: "Nice Shahghouse Biryani",
city: "Hyderabad",
category: "condo",
image: "http://via.placeholder.com/350x250",
numOfRooms: 4,
shared: true,
description: "Very nice apartment in center of the city.",
dailyPrice: 43
},
{
_id: 2,
title: "Modern apartment in center",
city: "Bangalore",
category: "apartment",
image: "http://via.placeholder.com/350x250",
numOfRooms: 1,
shared: false,
description: "Very nice apartment in center of the city.",
dailyPrice: 11
},
{
_id: 3,
title: "Old house in nature",
city: "Patna",
category: "house",
image: "http://via.placeholder.com/350x250",
numOfRooms: 5,
shared: true,
description: "Very nice apartment in center of the city.",
dailyPrice: 23
}]
}
}
render(){
const {rentals} = this.state;
return(
<div className="card-list">
<div className="container">
<h1 className="page-title">Your Home All Around the World</h1>
<div className="row">
{
rentals.map((rental)=>{
return(
<div key={rental._id} className="col-md-3">
<div className="card bwm-card">
<img
className="card-img-top"
src={rental.image}
alt={rental.title} />
<div className="card-body">
<h6 className="card-subtitle mb-0 text-muted">
{rental.shared} {rental.category} {rental.city}
</h6>
<h5 className="card-title big-font">
{rental.title}
</h5>
<p className="card-text">
${rental.dailyPrice} per Night · Free Cancelation
</p>
</div>
</div>
</div>
)
})
}
</div>
</div>
</div>
)
}
}
export default RentalHome;
Try this:
class First extends React.Component {
constructor (props){
super(props);
}
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>;
return (
<div>
{listItems}
</div>
);
}
}

Reactjs with Pokeapi

I have an app that searches the name of the pokemon reads the data eg. name, height, weight. Now when it comes to the abilities I can't get the value of the name of the ability.
here's my app.js
import React, { Component } from 'react';
import './App.css';
import Request from 'superagent';
class App extends Component {
constructor(props) {
super(props);
this.state = {
body: "",
value: "",
name: "",
abilities: "",
order: "",
weight: "",
height: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value.toLowerCase()});
}
handleSubmit(event) {
var url = "https://pokeapi.co/api/v2/pokemon/"+this.state.value;
Request.get(url).then((response) => {
this.setState({
body: response.body,
height: response.body.height,
weight: response.body.weight,
abilities: response.body.abilities,
name: response.body.name,
order: response.body.order,
picFront: response.body.sprites.front_default,
picBack: response.body.sprites.back_default,
picShiny: response.body.sprites.front_shiny,
});
});
event.preventDefault();
}
render() {
return (
<div className="flex">
<div className="App">
<h1>Search Pokemon</h1>
<form onSubmit={this.handleSubmit}>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
</div>
<div className="app2">
<h1><small>{this.state.order} </small>{this.state.name}</h1>
<img alt={this.state.name} src={this.state.picFront}/>
<img alt={this.state.name} src={this.state.picBack}/>
<img alt={this.state.name} src={this.state.picShiny}/>
<p>Height: {this.state.height}</p>
<p>Weight: {this.state.weight}</p>
<p>list of abilities here</p>
</div>
</div>
);
}
};
export default App;
Abilities has this structure. You can check this by logging this.state.abilities
abilities: [
{
slot: 3,
is_hidden: true,
ability: {
url: "https://pokeapi.co/api/v2/ability/31/",
name: "lightning-rod"
}
},
{
slot: 1,
is_hidden: false,
ability: {
url: "https://pokeapi.co/api/v2/ability/9/",
name: "static"
}
}
]
It's just an array. What you need to do is iterate over this array to retrieve those name values. There are a lot of ways to do this but the general logic should be something like:
"For each of these objects in the abilities array, I need to retrieve ability.name"
I am going to post some code here but try to solve it yourself before looking at mine.
In your p tag, you can do this
<p>
list of abilities here:
{this.state.abilities && this.state.abilities.map((abilityObject) =>
abilityObject.ability.name).join(', ')}
</p>

Categories

Resources