DropDown not populating data from API call - reactjs

I am new to React. I am trying to make an api call, and populate the data into a dropdown in react. The api call is successful and i am able to see the response also in network tab. but the response is not getting populated into the dropdown.
I want to populate "abc","def" etc from the json response onto my dropdown.
API Response
{
{
“ABC”: {
"detail": "/diagnosticWorkflows/abc”
},
“DEF”: {
"detail": "/diagnosticWorkflows/def”
},
“LMN”: {
"detail": "/diagnosticWorkflows/lmn”
},
“PQR”: {
"detail": "/diagnosticWorkflows/pqr”
}
}
UI Code
class ABC extends React.Component {
constructor(props) {
super(props);
this.state = {
diagnosticWorkflow: [],
selectedWorkflow: ""
};
}
componentDidMount(nextProps) {
api
.workflowsApi("https://localhost:8443/api/diagnostic-workflows/")
.then(res => {
let workflowFromApi = Object.keys(res).map(workflow => {
return { value: workflow, label: workflow };
});
this.setState({
diagnosticWorkflow: [
{ value: "", label: "(Select Your Workflow)" }
].concat(workflowFromApi)
});
})
.catch(error => {
Console.log(error);
});
}
render() {
return (
<div className={style.searchComponents}>
<div className={style.searchFilter}>
<label>
<span>Diagnostic Requests</span>
</label>
<Dropdown
auto={false}
source={this.state.diagnosticWorkflow}
allowBlank={false}
value={this.state.selectedWorkflow}
onChange={this.updateSearchParameters.bind(
this,
"selectedWorkflow"
)}
className={style.searchFilterDropdown}
/>
</div>
</div>
);
}
}

let workflowFromApi = Object.keys(res).map(workflow => {
return { value: workflow, label: workflow };
});
Your this code may not be working properly as res is {{....}} Object.keys(res) will be empty. Can you make the response as
{
“ABC”: {
"detail": "/diagnosticWorkflows/abc”
},
“DEF”: {
"detail": "/diagnosticWorkflows/def”
},
“LMN”: {
"detail": "/diagnosticWorkflows/lmn”
},
“PQR”: {
"detail": "/diagnosticWorkflows/pqr”
}
}
This way your Object.keys(res) will return an array of keys.

Related

Unable to read the json object stored in React state

import React from 'react';
import axios from 'axios';
class Quiz extends React.Component {
constructor(props) {
super(props);
this.state = {
showInstruction: true,
questionIndex: 0,
isLoading: true,
questions: ''
};
}
proceedHandler = () => {
this.setState({
showInstruction: false
})
}
handleQuestion = (event) => {
event.preventDefault();
console.log('show next question');
}
componentDidMount() {
console.log("After mount! Let's load data from API...");
axios({
method: "GET",
url: "/apis/questions"
}).then(response => {
console.log(response.data);
this.setState({ questions: response.data });
this.setState({ isLoading: false });
});
}
render() {
if (this.state.showInstruction) {
return (
<div>
<h1>Welcome to Quiz</h1>
<p>Quiz instructions goes here</p>
<button type="button" onClick={this.proceedHandler}>Proceed</button>
</div>
)
}
const { isLoading, questions } = this.state;
console.log(this.state['questions'][0]);
console.log(questions[0]);
if (isLoading) {
return <div className="App">Loading...</div>;
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<div onChange={this.onChangeValue}>
{/* {questions[0]} */}
</div>
<button onClick={this.handleQuestion}>Next</button>
</form>
</div>
)
}
}
export default Quiz;
My sample API content looks like the below. Right now making the api call to local file which is stored inside Public folder. Path is public/apis/questions.
[
{
id: 0,
question: `What is the capital of Nigeria?`,
options: [`New Delhi`, `Abuja`, `Aba`, `Onisha`],
answer: `Abuja`
},
{
id: 1,
question: `What is the capital of India?`,
options: [`Punjab`, `Awka`, `Owerri`, `Enugu`],
answer: `New Delhi`
}
]
I am building a quiz app and above is my code. I try to fetch the questions from api and render them one by one based on state. I am using axios to fetch the data inside componentDidMount and I can see the this.state.questions is updated with the questions array. But when I do questions[0] or this.state.questions[0], it always returns [. Any help would be greatly appreciated as I am fairly new the react development.
The issue is from my API data. I missed to wrap the keys with double quotes.
Updating the data from api call resolved my issue. So the sample api data will look like the below.
[
{
"id": 0,
"question": "What is the capital of Nigeria?",
"options": [
"New Delhi",
"Abuja",
"Aba",
"Onisha"
],
"answer": "Abuja"
},
{
"id": 1,
"question": "What is the capital of India?",
"options": [
"Punjab",
"Awka",
"Owerri",
"Enugu"
],
"answer": "New Delhi"
}
]

How to make a checkbox checked if a value exists in array of object in react

How can I make a checkbox checked if a value exists in array of object in reactjs ?
I have tried using includes function but it is not working.
I have array of object in employeeUnder key -
My array is -
"employeeUnder": [
{
"_id": "5d1a0a8a09b9cb0034d01aaf",
"employ": {
"_id": "5d120eba60093e02248d6a81",
"name": "Sehzan"
}
},
{
"_id": "5d1a0a8a09b9cb0034d01ab0",
"employ": {
"_id": "5d120eba60093e02248d6a83",
"name": "Sumit"
}
},
{
"_id": "5d1a0a8a09b9cb0034d01ab1",
"employ": {
"_id": "5d120eba60093e02248d6a7c",
"name": "Hariom"
}
}
],
I have to check if -
this.state.allemployees._id === employeeUnder.employ._id then checkbox must be checked.
My Code for input checkbox is -
if (this.state.allemployees && this.state.allemployees.length > 0) {
return (this.state.allemployees.map((employee) =>
<tr key={employee.empmainid}>
<td>{employee.empname}</td>
<td>{employee.empid}</td>
<td><input onChange={this.handleCheckbox} getUsername={employee.empname} className="" type="checkbox" checked name={employee.empmainid} value={employee.empmainid} /></td>
</tr>))
}
Right now all the checkbox are checked because I didn't apply the condition.
I want if a value exists in array of object then it must be checked otherwise NO.
Checkout this sandbox: https://codesandbox.io/s/blissful-edison-bjh0s
We'll be working with two arrays here:
allEmployees (never mutate)
employeesUnder (always update)
We can dynamically change the data inside employeesUnder through checking/toggling the corresponding input tag.
Essentially, inside the onChange() event, we will pass the id associated with an employee, if the input was already checked, that means it was already in the employeesUnder array. So we will use that id, to filter that employee out. The opposite would occur if the id was not found inside the array. So we would add the employee to employeesUnder.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
employeesUnder: [
{
_id: "5d1a0a8a09b9cb0034d01aaf",
employ: {
_id: "5d120eba60093e02248d6a81",
name: "Sehzan"
}
},
{
_id: "5d1a0a8a09b9cb0034d01ab0",
employ: {
_id: "5d120eba60093e02248d6a83",
name: "Sumit"
}
},
{
_id: "5d1a0a8a09b9cb0034d01ab1",
employ: {
_id: "5d120eba60093e02248d6a7c",
name: "Hariom"
}
}
],
allEmployees: [
{
_id: "3ds8f8ds9d8fds9f8a9f8afaf",
employ: {
_id: "eworweokrkowekoo34324234",
name: "Woofers"
}
},
{
_id: "5d1a0a8a09b9cb0034d01aaf",
employ: {
_id: "5d120eba60093e02248d6a81",
name: "Sehzan"
}
},
{
_id: "5d1a0a8a09b9cb0034d01ab0",
employ: {
_id: "5d120eba60093e02248d6a83",
name: "Sumit"
}
},
{
_id: "5d1a0a8a09b9cb0034d01ab1",
employ: {
_id: "5d120eba60093e02248d6a7c",
name: "Hariom"
}
}
]
};
handleCheck = id => {
const { allEmployees, employeesUnder } = this.state;
const employeesUnderIds = employeesUnder.map(employee => employee._id);
if (employeesUnderIds.includes(id)) {
//remove employee from employeesUnder list
const newArrWithRemovedEmployee = employeesUnder.filter(employee => {
return employee._id !== id;
});
this.setState({
...this.state,
employeesUnder: newArrWithRemovedEmployee
});
} else {
//add employee to employeesUnder list
const employeeIndex = allEmployees.findIndex(
employee => employee._id === id
);
const newArrWithAddedEmployee = [
...employeesUnder,
allEmployees[employeeIndex]
];
this.setState({
...this.state,
employeesUnder: newArrWithAddedEmployee
});
}
};
createList = () => {
const { allEmployees, employeesUnder } = this.state;
const employeesUnderIds = employeesUnder.map(employee => employee._id);
return allEmployees.map(employee => {
return (
<div>
<label>{employee.employ.name}: </label>
<input
type="checkbox"
value={employee._id}
checked={employeesUnderIds.includes(employee._id)}
onChange={() => this.handleCheck(employee._id)}
/>
</div>
);
});
};
render() {
return <div>{this.createList()}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

How to pass argument to function in reactjs?

How can I send sport_id form getSport to getEvents to show each sports events?
Can I put getSport function to other component, call and use it in this component?
events json:
[
{
"id": "912653",
"time": "1536471082",
"time_status": "1",
"league": {
"id": "900",
"name": "Hong Kong 2nd Division",
"cc": "hk"
},
"home": {
"id": "13767",
"name": "Yau Tsim Mong",
"image_id": "193606",
"cc": "hk"
},
"away": {
"id": "63770",
"name": "Tuen Mun SA",
"image_id": "56045",
"cc": "hk"
},
"timer": {
"tm": 74,
"ts": 25,
"tt": "1",
"ta": 0
},
"scores": {}
}
]
sports json:
[
{
"id": 8,
"name": "Rugby Union",
"is_active": null,
"slug": "rugby-union"
}
]
Here is my code:
import React, { Component } from "react";
import axios from "axios";
import moment from "moment";
export default class Feutred extends Component {
state = {
sports: [],
events: [],
isLoading: true,
errors: null
};
getSports() {
axios
.get("/api/v1/sports.json")
.then(response =>
response.data.map(sport => ({
id: sport.id,
name: sport.name,
slug: sport.slug
}))
)
.then(sports => {
this.setState({
sports,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
getEvents() {
axios
.get("/api/v1/events?sport_id=${sport_id}")
.then(response =>
response.data.map(event => ({
id: event.id,
time: event.time,
league: event.league,
time_status: event.time_status,
homeTeam: event.home,
awayTeam: event.away
}))
)
.then(events => {
this.setState({
events,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
componentDidMount() {
this.getSports();
(this.interval = setInterval(
() => this.getEvents({ time: Date.now() }),
12000
));
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const { sports, isLoading } = this.state;
return (
<React.Fragment>
{!isLoading ? (
sports.map(sport => {
const { id, name } = sport;
return (
<div key={sport.id}>
<div className="text">
<p className="meta">
<span className="matchinfo">
<span className="block">time</span>
<span className="block">timestatus</span>
</span>
</p>
<h3>
home-team vs aya tream
</h3>
<p className="league">
<a className="watchlive" href="">
<span className="icon" />
<span>Watch live</span>
</a>
<span>{sport.name} - league cc - league name</span>
</p>
</div>
</div>
);
})
) : (
<p>Loading...</p>
)}
</React.Fragment>
);
}
}
Just destructure it - load sports in one component then render some <EventsLoadingComponent /> passing sport id as prop ...
HINT: Use if(isLoading) return <p>Loading...</p> in render before 'main return' - no need to use ternary operator in return JSX.
UPDATE:
render() {
const { sports, isLoading } = this.state;
if(isLoading) return <p>Loading...</p>
return (
<React.Fragment>
{sports.map(sport => <EventsLoadingComponent sport={sport}/>}
</React.Fragment>
);
}
Move getEvents into <EventsLoadingComponent/> - you'll be fething for events related to this.props.sport.id and render them. This way each of them can be separately updated.
Remember to use key in the topmost html element.
UPDATE #2:
can you please give your code comparison with my code ?
Your code - linear, procedural, 'flat template-driven', forcing async to be sync, all-in-one-component ... while html is a (flatten view of) tree structure.
React thinking (generally, not my code only) - more OO, building tree of objects closer related to data and view structure, giving them own responsibility (data handling, view). Easier to read, expand (destructure further details to components - even one-liners), suitable to decorating, easy to manage ... and reuse.
Often object in structure renders only passed children (or nothing) only providing functionality. Available level of complexity is greater, communication within this structure is far easier (and less dependent) than (it could be done) in html.
Something like this:
getEvents({ id }) {
axios
.get(`/api/v1/events?sport_id=${id}`)
...
}
componentDidMount() {
this.getSports()
.then(() => {
return Promise
.all(this.state.sports.map(this.getEvents))
});
...
}
Note:
You need to refine the way you save the data because you need to know which events are for which sport.

TypeError: this.state.data.map is not a function despite setting data: [ ]

I'm trying to store the data in ItemLists component for which I need to first map over. But getting TypeError: this.state.data.map is not a function
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
fetch('items.json')
.then(function(response) {
if (response.status >= 400) {
throw new Error("Bad response");
}
return response.json();
})
.then(data => {
console.log(data)
this.setState({data: data});
});
}
render() {
return (
<div className="App">
{this.state.data.map(function(object){
return (
<ItemLists key={object.id} data={object}/>
)
})}
</div>
);
}
}
if I change the code to:
render() {
return (
<div className="App">
return (
<ItemLists data={this.state.data}/>
)
</div>
)
}
error I get is: Objects are not valid as a React child (found: object with keys {artist, title, level, released, rating}). If you meant to render a collection of children, use an array instead pointing towards this.setState({data: data});
json data:
{
"data": [
{
"name": "cnt",
"level": "12"
},
{
"name": "stewart",
"level": "6"
},
{
"name": "nic",
"level": "7"
}
]
}
After the fetch request is completed you set the data array in your state to the parsed JSON object. Since regular objects don't have a map method like arrays have, you get the error.
You could set the data in state to be the data array in the parsed JSON object instead.
componentDidMount() {
fetch('items.json')
.then(response => {
if (response.status >= 400) {
throw new Error("Bad response");
}
return response.json();
})
.then(response => {
this.setState({ data: response.data });
});
}
You should use Arrow function when you use the function in React Component
let JSONDATA =
{
data: [
{
name: "cnt",
level: "12"
},
{
name: "stewart",
level: "6"
},
{
name: "nic",
level: "7"
}
]
}
let { data } = JSONDATA;
data.map((obj)=>{
console.log(obj)
})

Uncaught TypeError - issue mapping over json object in React

I have a react component which is pulling in user data, and should be able to display one of the values from the JSON object. However, I'm getting the following Uncaught TypeError: this.state.reasons.map is not a function error. I believe its because it is expecting an array vs an object, but not quite sure how to check or convert as needed. It should be able to map over the object and render the value of subscription.current_period_end
This is the JSON object:
{
"displayName": "username",
"email": "user#email.com",
"entitled": true,
"id": "23456789",
"subscription": {
"canceled_at": 1508519952,
"current_period_end": 1524765490,
"is_premium": true,
"is_renewing": false,
"plan_type": "Annual",
"saved": true,
"status": "Active"
},
"country": "us",
"language": "en"
}
React Component
class CancelConfirm extends React.Component {
constructor(props) {
super(props)
this.state = {
reasons: []
}
this.processData = this.processData.bind(this)
}
componentDidMount() {
this.fetchContent(this.processData)
}
fetchContent(cb) {
superagent
.get('/api/user')
.then(cb)
}
processData(data) {
this.setState({
reasons: data.body
})
}
render(props) {
const content = this.props.config.contentStrings
const reason = this.state.reasons.map((reason, i) => {
return (
<p key={i}>{reason.subscription.current_period_end}</p>
)
})
console.log(reason)
return (
<div className = 'confirm' >
<p className = 'confirm-subpara' >{reason}</p>
</div>
)
}
}
export default CancelConfirm
Your this.state.reasons is response object from JSON, object doesn't have map function it's only for iterator values such as new Array() if you are trying to show reason you would simply do
Currently I don't know why you are trying to iterate over object when it has no array array data, maybe I got your question wrong so please specify why you even did map instead of simple object reference.
var API_RESPONSE = {
"body": {
"displayName": "username",
"email": "user#email.com",
"entitled": true,
"id": "23456789",
"subscription": {
"canceled_at": 1508519952,
"current_period_end": 1524765490,
"is_premium": true,
"is_renewing": false,
"plan_type": "Annual",
"saved": true,
"status": "Active"
},
"country": "us",
"language": "en"
}
}
class CancelConfirm extends React.Component {
constructor(props) {
super(props)
this.state = {
reasons: false
}
this.processData = this.processData.bind(this)
}
componentDidMount() {
this.fetchContent(this.processData)
}
fetchContent(cb) {
setTimeout(() => {
cb(API_RESPONSE)
}, 2000)
}
processData(data) {
this.setState({
reasons: data.body
})
}
render(props) {
if (!this.state.reasons) return (<i>Loading ....</i>)
const reason = <p>{this.state.reasons.subscription.current_period_end}</p>
return (
<div className = 'confirm' >
<p className = 'confirm-subpara' >{reason}</p>
</div>
)
}
}
ReactDOM.render(<CancelConfirm />, document.getElementById('react'))
<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="react"></div>
You can't map() over an Object. As such it would be better to do something like this:
render(props) {
const content = this.props.config.contentStrings
const { reasons } = this.state;
const keys = Object.keys(reasons);
return (
keys.map((k, i) => {
return <p key={i}>{reasons[k].subscription.current_period_end}</p>
})
)
}

Resources