Running into issues mapping information with React - javascript

I've been stuck on this problem for the longest time and haven't been able to make any headway on why I'm not able to map the values to the screen.
For testing purposes, when the code beneath the renderSurvey() console.log is commented out; the console will highlight two objects (one as an empty object and the second one as an array, after the data, is loaded from the database (picture).)
My running hypothesis is that the empty first object is causing the issues but I'm still stumped.
class SurveyList extends Component {
componentDidMount(){
this.props.fetchSurveys();
}
renderSurveys(){
console.log(this.props.surveys);
return this.props.surveys.map(survey => {
return (
<div className='card darken-1' key = {survey._id}>
<div className='card-content'>
<span className='card-title'>{survey.title}</span>
<p>
{survey.body}
</p>
<p className='right'>
Sent On: {new Date(survey.dateSent).toLocaleDateString()}
</p>
</div>
<div className='card-action'>
<a>Yes: {survey.yes}</a>
<a>No: {survey.no}</a>
</div>
</div>
)
})
}
render(){
return(
<div>
{this.renderSurveys()}
</div>
)
}
}
function mapStateToProps({ surveys }) {
return { surveys };
}
export default connect(mapStateToProps, { fetchSurveys })(SurveyList);

Try this:
render(){
if(this.props.surveys && this.props.surveys.length){
return(
<div>
{this.renderSurveys()}
</div>
)
}else{
return(
<div></div>
)
}
}
It will check if props.surveys is received and fulfilled then try run renderSurveys() in render().

Related

ReactJS not able to print nested values in rest api response

This is first time I am writing a React app and have create a sample app using create-react-app. I am calling a rest api using axios and it returns me json below which is nested. My component display the first level attributes but not nested one please help understand what I am doing wrong.
<Card type="inner" title="Social">
{social.dataall.map((social1,i) => (
<div key={i} class="card">
<div class="card-body">
<h5 class="card-title">Name: {social1.FullName}</h5>
{social1.Member1.map(function (member11, j) {
return <div key={j}> <h5>member11</h5></div>
{member11.User2.map(function (usr2, k) {
return <div key={k}> <h5>Type: {usr2.firstName}</h5></div>
})}
})}
</div>
</div>
))}
</Card>
JSON to parse and display:
{"app":{"dataall":[{"FullName":"Payment","DisplayName":"Payment","Id":"3366d5e59","Member1":[{"User2":[{"userId":"331322934","firstName":"fName1","lastName":"lName1"}],"Role3":[{"roleName":"Business"}]},{"User2":[{"userId":"331322934","firstName":"fName","lastName":"lName"}],"Role3":[{"roleName":"Owner"}]}]}]}}
Output:
Name: Payment
member11
member11
Not able to print the value for Type: {usr2.firstName}
You are closing the <div> element here:
return <div key={j}> <h5>member11</h5></div>
// ^^^^^^
That means the following code ({member11.User2...) is not part of the JSX element. It simply follows the return statement. Code after a returns statement is not executed:
(function() {
console.log("shown");
return;
console.log("not shown");
}());
You didn't explain how you want the UI to be structured but a simple solution would be to move the mapping inside the <div> element.
<div class="card-body">
<h5 class="card-title">Name: {social1.FullName}</h5>
{
social1.Member1.map(function (member11, j) {
return (
<div key={j}>
<h5>member11</h5>
{
member11.User2.map(function (usr2, k) {
return <div key={k}> <h5>Type: {usr2.firstName}</h5></div>
})
}
</div>
);
})
}
</div>

Connect two dropdown filters to one search button

I have two dropdowns that are filtering, but they filter as you drop them down and make selections. I have a search button that I would like to hook them both to. So you just saw a change in results once, after you pressed the button. I think i have all the logic i need here But im not sure exactly how to hook up the button
note: i know i have alot of logic in the render, but im just trying to make it work first
So far this is what I have:
constructor(props) {
super(props);
this.state = {
developers: [],
filterCountry: "All locations",
filterSkills: "All skills"
};
}
componentDidMount() {
fetch('API')
.then(features => features.json())
.then(developers => {
this.setState({ developers })
})
}
filterCountry(e){
this.setState({filterCountry: e })
}
filterSkills(e){
this.setState({filterSkills: e })
}
render() {
let developers = this.state.developers.features
if (!developers ){
return null
}
if (this.state.filterCountry && this.state.filterSkills) {
developers = developers.filter( developer => {
return this.state.filterCountry === 'All locations' ||
developer.properties.continent.includes(this.state.filterCountry)
});
developers = developers.filter( developer => {
return this.state.filterSkills === 'All skills' ||
developer.properties.skills.includes(this.state.filterSkills)
});
}
return (
<div>
<div>
<ControlSelect
onChange={this.filterCountry.bind(this)}
value={this.state.filterCountry}
options={options_dd1}
/>
</div>
<div className="inline-block mr24">
<ControlSelect
onChange={this.filterSkills.bind(this)}
value={this.state.filterSkills}
options={options_dd2}
/>
</div>
<button>Search</button>
</div>
<div>
<div>
{developers.map(developer => {
return (
<div key={developer.id}">
{developer.properties.name}
{developer.properties.description}
{developer.properties.skills}
</div>
</div>
</div>
)}
)}
)
any help would be greatly appreciated
The main problem with what you have is that once the filtering is done, there is no way to get the original list of developers back. You can create an 'original list' or developers and a new filteredList, which could be actually used by the render method to show data.
Basically, in your initial render, the developers key in your state is the default loaded from fetch and will get rendered in its entirety. Once you click the button, the doSearch method will modify the state and remove developers. This will cause the render to be called and show the new filtered list.
Otherwise, there's a few minor things things taht I have commented below.
constructor(props) {
super(props);
this.state = {
developers: [],
filterCountry: "All locations",
filterSkills: "All skills"
};
}
componentDidMount() {
fetch('API')
.then(features => features.json())
.then(developers => {
this.setState({ developers })
})
}
filterCountry(e){
this.setState({filterCountry: e })
}
filterSkills(e){
this.setState({filterSkills: e })
}
doSearch() {
// Create copy of state (you had a `.filtered` in your code, which doesn't make sense as developers is an array so it will have no `filtered` property unless you modified the prototype
let developers = this.state.developers.slice()
// This if block is pointless, because you start with a default state in the constructor (so unless your ControlSelect have a falsy option, this will always evaluate to `true`)
if (this.state.filterCountry && this.state.filterSkills) {
// THis will match EITHER country OR skills. You can change to && if wanted.
developers = developers.filter( developer => {
return this.state.filterCountry === 'All locations' ||
developer.properties.continent.includes(this.state.filterCountry) || this.state.filterSkills === 'All skills'
|| developer.properties.skills.includes(this.state.filterSkills)
});
this.setState({ developers })
}
}
render() {
return (
<div>
<div>
<ControlSelect
onChange={this.filterCountry.bind(this)}
value={this.state.filterCountry}
options={options_dd1}
value={this.state.filterCountry}
/>
</div>
<div className="inline-block mr24">
<ControlSelect
onChange={this.filterSkills.bind(this)}
value={this.state.filterSkills}
options={options_dd2}
value={this.state.filterSkills}
/>
</div>
<button onClick={this.doSearch.bind(this)}>Search</button>
</div>
<div>
<div>
{/* Now the developers contains stuff that was filtered in search */}
{this.state.developers.map(developer => {
return (
<div key={developer.id}>
{developer.properties.name}
{developer.properties.description}
{developer.properties.skills}
</div>
</div>
</div>
)}
)}
)

React cannot read property of undefined when accessing from e.g. IF statement

Redux turned my app state to props and I would like to access the properties of certain props once the API request is done. Yet, my page can't render as I get the above message.
kicking off GET reqest:
componentWillMount() {
this.props.renderRoles(this.props.params.roleID);
};
Rendering users does work though:
renderUsers(){
return this.props.roleDetails.userList.map((user)=>{
return(
<li className='list-group-item eventUserList' background-color="#f2f2f2" key={user._id}>
{user.userName}
</li>
);
});
};
render() {
const { handleSubmit, fields: {} } = this.props;
This is the problem part. Somehow, properties of this.props get undefined if I look for their properties:
if (this.props.roleDetails.roleName){
return (
If I only look for this.props.roleDetails, it does work, but not when for its properties:
if (this.props.roleDetails){
return (
<div className='container-fluid'>
<form>
<div className="roleDetails">
<div className="roleName">
{this.props.roleDetails.roleName}
</div>
</div>
<div className="roleUsers">
<div className="userDetails">
<h4 className="listOfUsers">Role Owner :</h4>
<ul>
{this.renderUsers()}
</ul>
</div>
</div>
<div>
<div className="submitRole" onClick={this.submitForRole.bind(this)}>Apply</div>
</div>
</form>
</div>
);
}else {
return (
<div>
</div>
);
}
}
}
function mapStateToProps(state) {
return{
roleDetails: state.api.roleDetails,
refreshedRole: state.api.refreshedRole
}
}
export default reduxForm({
form: 'roleView',
fields: []
}, mapStateToProps, actions)(roleView);
Other similar questions are related to functions, but in my case is about accessing props therefore .bind(this) doesn't work.
if (this.props.roleDetails.roleName) looks like roleDetails undefined befor You get data with request, so if You will try to get access to it's property roleName you will get an error.
if (this.props.roleDetails && this.props.roleDetails.roleName) shouldn't throw errors.
And it's not good idea to fetch data in componentWillMount, use componentDidMount instead.

I could not dispaly noroom component if there is no place name that user has queried

I have a search form where user search for place. If the place that user has typed is in the api (response object) then i want to display Room component else NoRoom component. When i type the place that is not in the api , my NoRoom component is not displayed.
search( query='place' ){
let url = "http://localhost:8000/api/v1/rental/?place__startswith="+encodeURIComponent(query);
Request.get(url).then((response) => {
console.log('response',response.body.objects);
this.setState({
place:response.body.objects,
});
});
}
searchUpdated(term){
this.search(term);
}
render() {
var margin = { marginTop : '13em' };
if (this.state.place){
let location = _.map(this.state.place, (place,id) => {
return(
<Room key={id}
slug={place.slug}
place={place.place}
city={place.city}
gallery={place.gallery}
property={place.property}/>
)
console.log('location',location);
});
let gallery = _.map(this.state.place, (place,id) => {
console.log('place',place.gallery);
_.map(place.gallery, (image,id) => {
return(
<img src={image.image} class="img-fluid" />
)
});
});
return(
<div className = "container">
<div className="content text-align-center">
<div className="row text-xs-center">
<div className="middle-text" style={margin}>
<h1 className="welcome"><span>Rental Space Welcome's you </span></h1>
<button ref="test" className="btn how-it-works" onClick={this.handleClick}>Search Space</button>
</div>
</div>
</div>
<div id="mySearch" className="overlay" onKeyDown={this.handleKeyDown}>
<button className="btn closebtn" onClick={this.handleClick}>x</button>
<div className="overlay-content">
<SearchInput ref="searchInput" className="search-input" onChange={this.searchUpdated} />
<div className="container searchList">
{ location }
</div>
</div>
</div>
</div>
);
}
else{
return(
<NoRoom />
)
}
}
}
class Room extends React.Component{
render(){
let imageFile = this.props.gallery.map((image) => {
return(
<img src={image.image} className="img-fluid" width="250px" height="250px" />
);
});
return(
<div className="room">
<div className="thumbnail">
{ imageFile[0] }
</div>
<h3 className="listingName text-left">
<a href = { "/rent/" + this.props.slug }>{this.props.place}</a>
</h3>
<span className="propertySpan">
<i className = "fa fa-home"></i>
<span className="property">{this.props.property}</span>
</span>
</div>
)
}
}
class NoRoom extends React.Component{
render(){
return(
<div>No Room</div>
)
}
}
You can see in image, No Room text is not displayed when there is no place called kat
What have i done wrong?
In you root-component, your local state (this.state.place) is an array that defines whether you display the Room component or the NoRoom component.
If your API doesn't find any matching place, then this.state.place is an empty array, which is truthy. That's why you just have to check the length of the array in your render method:
if (this.state.place.length > 0) {
Detailed explanation:
Your UI behavior is defined by the component state. The initial structure of this state must be described in your component, this is what you do in the component constructor:
this.state = {place: []};
This initial state will be used during the 1st rendering of the component.
Then, each time you want to update this state, you call this.setState() with a new value for the property "place", which must be an array for consistency.
In your render() method, you just have to describe your UI according to the current value of your state. Because "place" is always an array, the only thing you can do to check if you have data in it is to test the "length" property of this array (length === 0 when no data). If you only check the array itself like you did initially (if (this.state.place) { ... }), it will always evaluate to "true" (because an array, even empty, is always "truthy") and it's not what you want.

Any use of a keyed object should be wrapped in React.addons.createFragment(object)

let playerInfo = [{
name: 'jose',
country: 'USA',
age: 47
}];
export default class PlayersInfo extends React.Component {
static getProps() {
return {};
}
render() {
let playerInf = playerInfo.map((playerData, index) => {
return <div className="item" key={index}>{playerData}</div>
});
return <div>
<div>
{playerInf}
</div>
<RouteHandler />
</div>;
}
Why am I getting this error in the browser's console?
Warning: Any use of a keyed object should be wrapped in React.addons.createFragment(object) before being passed as a child.
I put together a JSBin for you. Basically, the warning comes from this line:
return <div className="item" key={index}>{playerData}</div>
It's because playerData is an object and ReactJS doesn't know how to render it.
If you change that to the following, it won't give you the warning:
return (
<div className="item" key={index}>
<div>Name: {playerData.name}</div>
<div>Country: {playerData.country}</div>
<div>Age: {playerData.age}</div>
</div>
);
Why am I getting this error in the browser's console?
Because you are passing an object (playerData) as child to a React Component.
I tend to run into this when rendering a Date object by mistake. You need to stringify these manually:
Produces warning:
<div>{ new Date() }</div>
Works ok:
<div>{ String(new Date()) }</div>
Does this work?
return <div>
<div>
{playerInf}
</div>
<div>
<RouteHandler />
</div>;
</div>
}

Categories

Resources