Related
I recursively mapped nested JSON, and console log output all elements in format: property => value correctly, but components do not render. Following is JSON:
{
"index": "dwarf",
"name": "Dwarf",
"speed": 25,
"ability_bonuses": [
{
"ability_score": {
"index": "con",
"name": "CON",
"url": "/api/ability-scores/con"
},
"bonus": 2
}
],
"alignment": "Most dwarves are lawful, believing firmly in the benefits of a well-ordered society. They tend toward good as well, with a strong sense of fair play and a belief that everyone deserves to share in the benefits of a just order.",
"age": "Dwarves mature at the same rate as humans, but they're considered young until they reach the age of 50. On average, they live about 350 years.",
"size": "Medium",
"size_description": "Dwarves stand between 4 and 5 feet tall and average about 150 pounds. Your size is Medium.",
"starting_proficiencies": [
{
"index": "battleaxes",
"name": "Battleaxes",
"url": "/api/proficiencies/battleaxes"
},
{
"index": "handaxes",
"name": "Handaxes",
"url": "/api/proficiencies/handaxes"
},
{
"index": "light-hammers",
"name": "Light hammers",
"url": "/api/proficiencies/light-hammers"
},
{
"index": "warhammers",
"name": "Warhammers",
"url": "/api/proficiencies/warhammers"
}
],
"starting_proficiency_options": {
"choose": 1,
"type": "proficiencies",
"from": [
{
"index": "smiths-tools",
"name": "Smith's tools",
"url": "/api/proficiencies/smiths-tools"
},
{
"index": "brewers-supplies",
"name": "Brewer's supplies",
"url": "/api/proficiencies/brewers-supplies"
},
{
"index": "masons-tools",
"name": "Mason's tools",
"url": "/api/proficiencies/masons-tools"
}
]
},
"languages": [
{
"index": "common",
"name": "Common",
"url": "/api/languages/common"
},
{
"index": "dwarvish",
"name": "Dwarvish",
"url": "/api/languages/dwarvish"
}
],
"language_desc": "You can speak, read, and write Common and Dwarvish. Dwarvish is full of hard consonants and guttural sounds, and those characteristics spill over into whatever other language a dwarf might speak.",
"traits": [
{
"index": "darkvision",
"name": "Darkvision",
"url": "/api/traits/darkvision"
},
{
"index": "dwarven-resilience",
"name": "Dwarven Resilience",
"url": "/api/traits/dwarven-resilience"
},
{
"index": "stonecunning",
"name": "Stonecunning",
"url": "/api/traits/stonecunning"
},
{
"index": "dwarven-combat-training",
"name": "Dwarven Combat Training",
"url": "/api/traits/dwarven-combat-training"
},
{
"index": "tool-proficiency",
"name": "Tool Proficiency",
"url": "/api/traits/tool-proficiency"
}
],
"subraces": [
{
"index": "hill-dwarf",
"name": "Hill Dwarf",
"url": "/api/subraces/hill-dwarf"
}
],
"url": "/api/races/dwarf"
}
Then this is code:
import React, {Component} from 'react'
import { Grid, Header, Label } from 'semantic-ui-react'
import TypeComponent from './type_component'
import TestComponent from './test_component'
class raceWindow extends Component {
constructor(props)
{
super(props)
this.state = {
data: {}
}
}
componentDidMount()
{
fetch(this.props.hdAPI)
.then(response=>response.json())
.then(data => {this.setState({data: data})});
this.setState({hdAPI: this.props.hdAPI});
}
componentDidUpdate(prevProps)
{
if(this.props.hdAPI !== prevProps.hdAPI)
{
fetch(this.props.hdAPI)
.then(response=>response.json())
.then(data => {this.setState({data: data})});
this.setState({hdAPI: this.props.hdAPI});
}
}
isType (attr, value)
{
if(Array.isArray(value))
{
value.map((v) => {
Object.entries(v).map(([a1,v1]) => this.isType(a1,v1))
})
}
else
{
if(typeof value === 'object')
{
Object.entries(value).map(([a,v]) => this.isType(a,v))
}
else
{
console.log(attr);
console.log(value);
return(<Grid.Column><Label>{attr}</Label>{value}</Grid.Column>);
}
}
};
render()
{
const { data} = this.state;
//I also tried to do the recursive map in a component, but it does not work either
/*
return(
<div>
<Grid container columns = {10}>
<TypeComponent attr = {""} value = {data} />
</Grid>
</div>
);*/
return(
<div>
<Grid container columns = {10}>
{this.isType("",data)}
</Grid>
</div>
);
}
}
export default raceWindow
When I tried to map it manually in the first layer, only first layer components rendered. Nested data in deeper layers do not render, though console output the deeper data correctly. So I assume react do not render deeper component. How should I deal with this?
Edit: Just note, I may be wrong, but I think every loop of isType() will go to this part of code at last:
else
{
console.log(attr);
console.log(value);
return(<Grid.Column><Label>{attr}</Label>{value}</Grid.Column>);
}
console also output all non-array, non-object value in the log, but return() in this block does not render.
First let's look at the main problems you have right now:
The if blocks inside your isType function do not return anything. You are running functions against the value argument, but then you're not doing anything with what those functions return. Fixing this can be as simple as changing value.map... to value = value.map..., but if you don't do something, then when you get to the return at the end, those if blocks will have no impact on the final result.
Inside the first if block, your value.map... function doesn't return anything since you have brackets around Object.entries.... You just need to return Object.entries..., or else remove the brackets.
If we fix those problems and simplify the if/else logic a bit, we end up getting something back in the JSX:
isType(attr, value) {
let returnValue = value;
if (Array.isArray(returnValue)) {
returnValue = value.map((v) => {
return Object.entries(v).map(([a1, v1]) => this.isType(a1, v1));
});
} else if (typeof returnValue === "object") {
returnValue = Object.entries(value).map(([a, v]) => this.isType(a, v));
}
return (
<Grid.Column>
<Label>{attr}</Label>
{returnValue}
</Grid.Column>
);
}
But you'll see when you get this far that the resulting DOM probably isn't what you want.
Instead, I recommend changing the approach a bit so that we first convert our JSON into a structure we can use and then map over it in our render function. This will hopefully also make it a little easier to reason about what is happening when and to modify your function to only add things to the final output that you actually care about.
That'd look something like this:
class RaceWindow extends Component {
constructor(props) {
super(props);
// your JSON; store in state if necessary
this.data = {};
// create a placeholder variable
this.finalDom = [];
// call `this.isType` to fill in that variable
// the result is a giant array of shape {attr: 'str', value: 'str'}
this.isType("", this.state.data);
}
isType(attr, value) {
if (typeof value === "string") {
this.finalDom.push({
attr,
value
});
}
if (Array.isArray(value)) {
this.finalDom.push({
attr,
value: value.map((v) => {
return Object.entries(v).map(([a1, v1]) => this.isType(a1, v1));
})
});
}
if (typeof value === "object") {
this.finalDom.push({
attr,
value: Object.entries(value).map(([a, v]) => this.isType(a, v))
});
}
}
render() {
return (
<div>
<div class="container">
{this.finalDom.map((obj) => (
<div class="item">
<div class="label">{obj.attr}</div>
{obj.value}
</div>
))}
</div>
</div>
);
}
}
CodeSandbox demo of the above.
Final note: Make sure you capitalize the first letter of your component, or else React won't recognize it as a component.
Following is working code:
import React, {Component} from 'react'
import { Grid, Header, Label } from 'semantic-ui-react'
import TypeComponent from './type_component'
import TestComponent from './test_component'
class raceWindow extends Component {
constructor(props)
{
super(props)
this.state = {
data: {}
}
}
componentDidMount()
{
fetch(this.props.hdAPI)
.then(response=>response.json())
.then(data => {this.setState({data: data})});
this.setState({hdAPI: this.props.hdAPI});
}
componentDidUpdate(prevProps)
{
if(this.props.hdAPI !== prevProps.hdAPI)
{
fetch(this.props.hdAPI)
.then(response=>response.json())
.then(data => {this.setState({data: data})});
this.setState({hdAPI: this.props.hdAPI});
}
}
isType (attr, value, obj)
{
if(Array.isArray(value))
{
value.map((v) => {
Object.entries(v).map(([a1,v1]) => this.isType(a1,v1,obj))
})
}
else
{
if(typeof value === 'object')
{
Object.entries(value).map(([a,v]) => this.isType(a,v,obj))
}
else
{
var pair = {}
pair[attr] = value
obj.push(pair);
}
}
};
render()
{
const { data} = this.state;
var obj = [];
return(
<div>
<Grid container columns = {5}>
{this.isType("",data,obj)}
{
obj.map((arr, i) => {
return(
Object.entries(arr).map(([a,v]) => {
return(
<Grid.Column key = {i}><Label key = {i} color = 'orange'>{a}</Label>{v}</Grid.Column>
);
})
);
})
}
</Grid>
</div>
);
}
}
export default raceWindow
Thanks cjl750's advice, I use an variable to collect results from recursive function and it works. I also tried to let very if block has a return as his advice, but components still do not render. Not sure why.
For now, it seems jsx returned from deep layer in recursive function do not render.
I created a child component by react-select, but options don't show up in first click on selector.
For the second time each section is clicked, the options are displayed. I tried to use the AsyncSelect but again it did not work.
The Data is read from Local Storage, but I don't think there is a problem with this.
Sandbox:
https://codesandbox.io/s/strange-field-4elv3?file=/src/App.js
My data from local storage:
const feauters = [
{
"id": 5,
"title": "Type",
"options": [
{
"id": 231040,
"name": "cloth",
"property": 5
},
{
"id": 230081,
"name": "Synthetic materials",
"property": 5
}
]
},
{
"id": 646,
"title": "Shoe soles",
"options": [
{
"id": 231063,
"name": "Abrasion resistant",
"property": 646
},
{
"id": 231064,
"name": "Reduce the pressure",
"property": 646
}
]
},
]
Parent Component:
<MultiSelect features={features} />
My Component:
import React, {useEffect, useState} from 'react';
import {Form} from 'react-bootstrap';
import Select from 'react-select';
const MultiSelect = ({features}) => {
// Declare States
const [selectors, setSelectors] = useState([]);
// Handle Features
useEffect(() => {
const initialSelectors = features.map((item, index, array) => {
const options = item.options.map((subItem) => {
return {
value: `${subItem.property}-${subItem.id}`,
label: subItem.name,
};
});
return (
<React.Fragment key={`product-multiselect-${index}-${item.id}`}>
<Form.Label htmlFor={`product-multiselect-${index}-${item.id}`}>{item.title}</Form.Label>
<Select
id={`product-multiselect-${index}-${item.id}`}
className="mb-2"
classNamePrefix="select"
defaultInputValue="Select..."
placeholder="Select..."
noOptionsMessage={() => 'Not Found.'}
isMulti
isClearable
isRtl
isSearchable
name={item.title}
onChange={handleChangeInput}
options={options}
/>
</React.Fragment>
);
});
setSelectors(initialSelectors);
}, [features]);
// Handle Change Input
const handleChangeInput = (values) => {
console.log(values);
};
return selectors;
};
export default MultiSelect;
First of all as mentioned in the comments you shouldn't store the component inside the state. Related question
Secondary, options don't show up because of defaultInputValue props. If you remove it, the component would work as intended
In React, I'm using Axios to map an array to output a list of movie names that are pulling from the MovieDB API. It connects to the MovieDB just fine, however, I get the following error in the console:
Uncaught TypeError: this.state.movies.map is not a function
I believe this is preventing the movie list from outputting to the browser.
Codesandbox link is here.
Here's the SearchBar component where the code lies:
import React, { Component } from "react";
import TextField from "#material-ui/core/TextField";
import axios from "axios";
import "../SearchBar/_search-bar.scss";
class SearchBar extends Component {
state = {
userSearchTerm: "",
movies: []
};
// When user types, match the value to state
onInputChange = e => {
this.setState({ userSearchTerm: e.target.value });
};
// On submitting the input, grab the API
onInputSubmit = e => {
e.preventDefault();
const movieName = this.state.userSearchTerm;
const KEY = "XXXXXXXXXX";
const searchQuery = `https://api.themoviedb.org/3/search/movie?api_key=${KEY}&language=en-US&query=${movieName}&page=10`;
axios.get(searchQuery).then(res => {
this.setState({ movies: res.data });
});
};
render() {
return (
<div>
<form onSubmit={this.onInputSubmit}>
<TextField
label="Search for a movie and hit enter..."
margin="normal"
className="search-bar"
onChange={this.onInputChange}
/>
</form>
<ul>
{this.state.movies.map(movie => (
<li key={movie.id}>{movie.results.title}</li>
))}
</ul>
</div>
);
}
}
export default SearchBar;
On a side note, I tested out this same code, but with a different API and it worked just fine. Is there something wrong with the API itself or the this.state.movies.map?
The API you are using is returning an object with "results" being the key you are looking for. If you update your setState to this.setState({ movies: res.data.results }); you should get what you are looking for.
Axios Response Schema
As a side note I would guard your map function with something like {Array.isArray(this.state.movies) && this.state.movies.map(movie => (... this will conditionally render the output only once movies is set in state and is an array.
A working code:
import React, { Component } from "react";
import TextField from "#material-ui/core/TextField";
import axios from "axios";
import "../SearchBar/_search-bar.scss";
class SearchBar extends Component {
state = {
userSearchTerm: "",
movies: []
};
// When user types, match the value to state
onInputChange = e => {
this.setState({ userSearchTerm: e.target.value });
};
// On submitting the input, grab the API
onInputSubmit = e => {
e.preventDefault();
const movieName = this.state.userSearchTerm;
const KEY = "XXXXXX";
const searchQuery = 'https://api.themoviedb.org/3/search/movie?api_key=${KEY}&language=en-US&query=${movieName}&page=10';
axios.get(searchQuery).then(res => {
console.log("res is ------", res)
this.setState({ movies: res.data.results });
});
};
render() {
return (
<div>
<form onSubmit={this.onInputSubmit}>
<TextField
label="Search for a movie and hit enter..."
margin="normal"
className="search-bar"
onChange={this.onInputChange}
/>
</form>
<ul>
{this.state.movies.map(movie => (
<li key={movie.id}>{movie.original_title}</li>
))}
</ul>
</div>
);
}
}
export default SearchBar;
You should use this.setState({ movies: res.data.results }); and <li key={movie.id}>{movie.original_title}</li>
Let me know if it works.
You need to change the setState call to this:
this.setState({ movies: res.data.results });
The response is not an array, it is an object like this:
{
"page": 10,
"total_results": 136643,
"total_pages": 6833,
"results": [
{
"vote_count": 110,
"id": 13189,
"video": false,
"vote_average": 7.3,
"title": "A Christmas Carol",
"popularity": 6.52,
"poster_path": "/m3T3iLdE6J5PrqvvP0XNHBvM2bm.jpg",
"original_language": "en",
"original_title": "A Christmas Carol",
"genre_ids": [
18,
10751,
14,
10770
],
"backdrop_path": "/gaTpxTYQMGoagtMVYK8F7SjqTGM.jpg",
"adult": false,
"overview": "An old bitter miser who makes excuses for his uncaring nature learns real compassion when three ghosts visit him on Christmas Eve.",
"release_date": "1984-12-17"
},
{
"vote_count": 419,
"id": 12103,
"video": false,
"vote_average": 6.1,
"title": "Don't Say a Word",
"popularity": 9.995,
"poster_path": "/qx3hgW9MqxsEEFjx4eSbpp1Fe2l.jpg",
"original_language": "en",
"original_title": "Don't Say a Word",
"genre_ids": [
53
],
"backdrop_path": "/AaOtoMzqWJPSNXPRKwbvqf6MbKo.jpg",
"adult": false,
"overview": "When the daughter of a psychiatrist is kidnapped, he's horrified to discover that the abductors' demand is that he break through to a post traumatic stress disorder suffering young woman who knows a secret..",
"release_date": "2001-09-28"
}
]
}
I'm new to React-Native and I'm learning this by tutorials and examples all over the web. I am trying to do something very simple but it has been a week since I hit this problem and after digging StackOverflow and many other contents, none of them could help. So I decided to ask it directy. My apologies if the question looks duplicate or it seems silly.
I am trying to iterate over a JSON object and display it. All I want to do right now is to show each JSON object with its title (username). I'm planning to do much more - make the title a button and show the details of user after hitting button - but right now this is the big rock I've hit into.
Here is my code. Please note my comment on fetchdata method :
import React, { Component } from 'react'
import { View, Text, FlatList, TouchableOpacity, ListView } from 'react-native'
class MyListItem extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
const textColor = this.props.selected ? "red" : "black";
return (
<TouchableOpacity onPress={this._onPress}>
<View>
<Text style={{ color: textColor }}>
{this.props.title}
</Text>
</View>
</TouchableOpacity>
);
}
}
export default class HttpExample extends Component {
constructor(props) {
super(props);
this.state = {
data: '',
username: [],
first_name: '',
last_name: ''
};
//Using ES6 we need to bind methods to access 'this'
this.fetchData = this.fetchData.bind(this);
}
componentDidMount() {
this.fetchData();
}
fetchData() {
// The first line - which is commented - returns all of non-admin
// users, the second one returns only one user. Note that the
// second one works fine and the first one does not.
// fetch('http://URL/users.json', {
fetch('http://URL/users/12345678001.json', {
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
data: responseJson,
username: responseJson.username,
first_name: responseJson.first_name,
last_name: responseJson.last_name
})
})
.catch((error) => {
console.error(error);
});
}
_keyExtractor = (item, index) => item.id;
_onPressItem = (id: string) => {
// updater functions are preferred for transactional updates
this.setState((state) => {
// copy the map rather than modifying state.
const selected = new Map(state.selected);
selected.set(id, !selected.get(id)); // toggle
return {selected};
});
};
_renderItem = ({item}) => (
<MyListItem
id={item}
onPressItem={this._onPressItem}
title={this.state.username}
/>
);
render() {
return (
<FlatList
data={[this.state.data]}
renderItem={this._renderItem}
/>
)
}
}
And here is a sample of one of my JSON objects created by Django ReST framework, written by myself. I've just simplified the object a bit and removed some of the fields, for better reading (The avatar field is a base64 image field and it is much smaller than the original one):
{
"username": "12345678003",
"email" : "sample#gmail.com",
"first_name": "AAA",
"last_name": "BBB",
"phone_number": "12045678000",
"gender": "M",
"city": "NY",
"description": "",
"date_of_birth": "2010-03-28",
"avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPBhAREQ8QDQ8PDxIWEBAWFRANDxAQFRUWFhURExUYHSggGBolGxUTITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAOEA4QMBIgACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABAUBAgMGB//EADIQAQABAwEHAQYEBwAAAAAAAAABAgMRBAUSITFBUXFhIjKRobHBM4HR8RMjQmJyguH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/APpgCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDIAAAAAAAAAAAAAAAAAAAAAAANrduaq8RGZkGqVY0FdXON2PXn8E/SaOKIzPGrv0jwlAhW9nURHHNU/B3p0tER7sOwDjVpaJj3YcbmzqJjhmJTAFPf0FdPGPaj5/BEejRtV",
"groups": [1,2],
"user_permissions": [],
"interests": [1,2]
}
The above is what I get by calling http://URL/users/12345678001.json which returns one user. I have been able to show the user as one touchable opacity in the application (the above code works) But when I call users.json which has a structure like below:
[{user1 data},{user2 data}, etc.]
I cannot make the mobile application display each user's username in the mobile output. Either I get nothing (nothing is displayed) or the usual errors pop up (TypeError: object is not a function, etc.) I want to have the application iterate through the whole users.json and show each user's username as one touchable opacity. I've tried using .map which throws me the error, or calling each object in responseJson by their array index which either shows me nothing or throws error.
Let me know about your ideas and solutions.
FYI, I am testing this on my Nexus 5X phone directly.
Update
Here is an example of users.json as it reflects in my console log :
[{
"username": "12345678001",
"email" : "sample#gmail.com",
"first_name": "AAA",
"last_name": "BBB",
"phone_number": "12045678000",
"gender": "M",
"city": "NY",
"description": "",
"date_of_birth": "2010-03-28",
"avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPBhAREQ8QDQ8PDxIWEBAWFRANDxAQFRUWFhURExUYHSggGBolGxUTITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAOEA4QMBIgACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABAUBAgMGB//EADIQAQABAwEHAQYEBwAAAAAAAAABAgMRBAUSITFBUXFhIjKRobHBM4HR8RMjQmJyguH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/APpgCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDIAAAAAAAAAAAAAAAAAAAAAAANrduaq8RGZkGqVY0FdXON2PXn8E/SaOKIzPGrv0jwlAhW9nURHHNU/B3p0tER7sOwDjVpaJj3YcbmzqJjhmJTAFPf0FdPGPaj5/BEejRtV",
"groups": [1,2],
"user_permissions": [],
"interests": [1,2]
},
{
"username": "12345678003",
"email" : "sample#gmail.com",
"first_name": "AAA",
"last_name": "BBB",
"phone_number": "12045678003",
"gender": "M",
"city": "NY",
"description": "",
"date_of_birth": "2010-12-20",
"avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPBhAREQ8QDQ8PDxIWEBAWFRANDxAQFRUWFhURExUYHSggGBolGxUTITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAOEA4QMBIgACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABAUBAgMGB//EADIQAQABAwEHAQYEBwAAAAAAAAABAgMRBAUSITFBUXFhIjKRobHBM4HR8RMjQmJyguH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/APpgCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDIAAAAAAAAAAAAAAAAAAAAAAANrduaq8RGZkGqVY0FdXON2PXn8E/SaOKIzPGrv0jwlAhW9nURHHNU/B3p0tER7sOwDjVpaJj3YcbmzqJjhmJTAFPf0FdPGPaj5/BEejRtV",
"groups": [1,2],
"user_permissions": [],
"interests": [1,2]
}]
Another Update
As requested, here is a screenshot of my console, note that it differs from what I've posted here and has many different fields :
More Investigation:
I decided to make the code much simpler, and just focus on the main problem. How to return the iterated object for display:
My code is now this:
import React, { Component } from 'react'
import { View, Text, ListView } from 'react-native'
export default class HttpExample extends Component {
constructor(props) {
super(props);
data = [''];
}
fetchData() {
fetch('http://URL/users.json', {
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
console.log("ResponseJson is :" + responseJson);
console.log(responseJson.users.length);
console.log(responseJson.users.username);
console.log(responseJson.users);
console.log("THIS:::" + responseJson.users[0].username);
responseJson.users.map((user) =>
{
console.log("THIS:::" + user.username);
data.push(user.username);
console.log("This.data:" + data[0] + "second:" + data[1]);
});
return data;
})
.catch((error) => {
console.error(error);
});
}
_keyExtractor = (item, index) => item.id;
render() {
return (
<View>
<ListView
dataSource={this.fetchData()}
renderRow={(data) => <Text>{data.username}</Text>}
/>
</View>
)
}
}
All of the "console.log" commands return with correct info (Check the end of the post), but my render does not work and throws undefined is not a object. I really don't understand what is the exact problem? isn't data an array? Why it can't be displayed and throws TypeError? What am I getting wrong?
console.log outputs, in correct order :
ResponseJson is :[object Object]
3
undefined
(whole users.json is returned)
THIS:::12345678001
THIS:::12345678001
This.data:second:12345678001
THIS:::12345678002
This.data:second:12345678001
THIS:::12345678003
This.data:second:12345678001
I need a little help or suggestion how I can get this to work - I have more that one set of group of radio buttons
The first group checks and unchecks and shows the correct input but when I check the second group the first group is unchecked and the input of the second group is shown.
I want the first group of radio button to not uncheck when the second group of radio is checked. Its strange both of the names groups are different not why it unchecking previous groups. Thought the checked controlled would be better use to tracked when checked. Can any help in solving my problem.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class MainForm extends Component {
constructor(props) {
super(props);
this.state = {
isChecked: null,
isHidden: true,
selectedOption: null,
checkedState: null,
};
}
handleCheckboxToggle(name, event) {
if (event.target.checked) {
this.setState({
checkedState: event.target.value,
isHidden: false,
selectedOption: event.target.name,
});
} else {
this.setState({
isHidden: true,
selectedOption: null,
});
}
}
renderCheckboxComponent(answer) {
return (
<div className="form__field--wrapper">
{
answer.map((item, index) => (
<div key={item.id} className="form__checkbox form__checkbox--inline">
<label className="form__checkbox required" htmlFor={`item.label-${item.name}-${index}`}>
{item.label}
</label>
<input
type="radio"
id={`item.label-${item.name}-${index}`}
name={item.name}
value={item.value}
onClick={e => this.handleCheckboxToggle(item.name, e)}
checked={this.state.checkedState === item.value}
/>
<span />
</div>
))
}
</div>
);
}
renderFieldInputComponent(fields, name) {
return (
<div>
{
fields.map(item => (
this.state.selectedOption === item.name ?
<div key={item.id} className="form__row">
<label className="required" htmlFor={name}>
{item.label}
</label>
<input
type={item.type}
id={name}
name={name}
required
placeholder={item.placeholder}
/>
<span />
</div> : ''
))
}
</div>
);
}
render() {
const { data } = this.props;
const { isHidden, isChecked } = this.state;
return (
<form>
{
data.map(item => (
<div key={item.id} className="form__row form__row--checkbox">
<p className="form__row--label">{item.name}</p>
{ this.renderCheckboxComponent(item.answer)}
{ !isHidden && this.renderFieldInputComponent(item.field, item.category)}
</div>
))
}
<button type="button" className="btn btn--red">Update Preferences</button>
</form>
);
}
}
MainForm.propTypes = {
data: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
}).isRequired).isRequired,
};
export default MainForm;
This is the data I am working with
{
"Questions":[
{
"id":1,
"name":"Email",
"category":"emailPermission",
"answer":[
{
"id":10,
"label":"Yes",
"value":"emailPermissionYes",
"name":"emailPermission",
"checked": true
},
{
"id":11,
"label":"No",
"value":"emailPermissionNo",
"name":"emailPermission",
"checked": true
}
],
"field":[
{
"id": 12,
"type": "email",
"name": "emailPermission",
"label": "Enter your email address",
"placeholder": "example#email.com",
"required": false
}
]
},
{
"id":2,
"name":"Post",
"category":"postPermission",
"answer":[
{
"id": 21,
"label":"Yes",
"value":"postPermissionYes",
"name":"postPermission",
"checked": true
},
{
"id": 22,
"label":"No",
"value":"postPermissionNo",
"name":"postPermission",
"checked": false
}
],
"field":[
{
"id": 23,
"type": "text",
"name": "postPermission",
"label": "Postcode",
"placeholder": "SE1 7TP",
"required": true
}
]
},
}
The error is not in how you've handled name, but in how you handle the checked state.
Whenever a radio is checked, this.state.checkedState changes to the currently selected radio's value.
This means that when one selects a radio button from Question 2, you'll end up with a state similar to {checkedState: 'postPermissionYes'}.
When React then re-renders all the checkboxes, only the most recently clicked one will pass the checked={this.state.checkedState === item.value} test. The previously clicked radio would return false for the checked={this.state.checkedState === item.value}.
Instead, I recommend to handle checkedState slightly differently…
Store each radio group's value into a state property, you can use the name value for each one.
This means that you'd store the state as:
this.state = {
emailPermission: null,
postPermission: null,
// other properties would go here as well...
}
and your handler would look as follows:
handleCheckboxToggle(name, event) {
this.state[name] = event.target.value;
}
lastly, the radio would determine the checked attribute as follows:
checked={this.state[item.name] === item.value}
I hope this helps!