I'm working with React and actual live data from a database for the first time. I'm using fetch as outlined in this article and that's working fine. I've been able to get data received from a php file to print into react.
I've run now into trouble because React kind of stopped making sense. Some variables will work just fine, while others that use the exact same data will not.
For example:
Given this array of objects
I can just do this to assign it to a variable:
var posts = this.props.postData.map(function(entry, index){
return <li>{entry.post_title}</li>;
})
And it will output just fine to this:
However, in the same function as the above, if I wanted to assign a specific string from the object to a variable, suddenly react will tell me the object is undefined.
var singlepost = <span>{this.props.postData[0].post_content}</span>
var singlepost = this.props.postData[0].post_content;
and even:
var singlepost = this.props.postData[0];
return (
<div>{singlepost.post_content}</div>
)
Will result in this:
No matter what I try React keeps telling me it's undefined, even though if I console.log the object right before using it its content will show in the console just fine. As soon as I specify the string I want, I will get an undefined error.
Is there a specific of doing this?
If your array is empty initially you need to check for that. You can do that with length:
if (this.props.postData.length) {
const { post_content } = this.props.postData[0];
return <div>{post_content}</div>;
}
return <div>No data</div>;;
you can do something like this
var singlepost = this.props.postData[0];
let post = null;
if(singlepost){
post = <div>{singlepost.post_content}</div>
}
return (
post
)
Maybe postData in first render is empty array. Just Add condition to render :
if(!this.props.postData.length) {
return null;
}
var singlepost = this.props.postData[0];
return (
<div>{singlepost.post_content}</div>
);
I prefer to do this way because get invalid cases out of the way first give code more clean.
You could also better your null check this way. It is a bit more verbose, but I appreciate the safety of it. It's also a bit of duplication
if (this.props.postData && this.props.postData.length > 0) {
// Do your computation here
}
Let me know if this meets your needs!
Related
I have a function that I'm including on each line of my component that looks at and filters off of the returned number of records with a specific status and then takes the length of that array and displays it on the screen.
{props.locations.filter(x => x.details && x.details.status === "NEVER").length}
Currently there are only 3 separate statuses that a location could possibly have. Because of that, I'm trying to create a separate function that looks at whatever status is passed in instead of hard coding it for each line in my component. I recognize that this might be a really basic question, but does any have any idea how to do this?
The nicest is to able to partially apply status, so lets put it as a first argument
const filter = status => locations => {
return locations.filter(x => x.details && x.details.status === status).length;
}
Now we can make nice filters for every status
const neverFilter = filter('NEVER');
const progressFilter = filter('PROGRESS')
// and use it
const results = neverFilter(locations);
If you need it can be used also in one line, so its very flexible construct
const results = filter('NEVER')(locations);
PS. I didn't append any TS types as you did not put any in your question, but if u use TS, status should be some kind of union like type Status = 'NEVER' | 'Other', and location should be kind of Record type. Just saying :)
Something like this:
filter = (locations, status) => {
return locations.filter(x => x.details && x.details.status === status).length;
}
And call the function:
this.filter(props.locations, 'NEVER');
Edit: the code below was made up on the spot to show how I was going about what I was doing. It definietely won't run, it is missing a lot of things.
Here is a working example in codepen: https://codepen.io/goducks/pen/XvgpYW
much shorter example: https://codepen.io/goducks/pen/ymXMyB
When creating a function that is using call or apply, the this value stays null when using getPerson. however, when I use apply or call with getPerson it returns the correct person.
Please critique, I am really starting to learn more and more. I am in the middle of a project section so it might be hard to change all the code, but my next project could implement this better.
call and apply are setting to the window and not the object.
I will provide code that is much simpler with the same concept of what I am talking about.
function createPerson(){
this.manager = null;
this.teamManager = null;
this.setTeamManager = function(val){
this.teamManager = val;
}
this.setManager = function(val){
console.log('setting manager to',val);
this.teamManager = val;
}
this.getTeamManager = function(){
console.log('setting team manager to',val);
return this.teamManager ;
}
this.getManager = function(){
return this.manager;
}
this.appendSelect = function(elem){
var that = this;
createOtherSelects(that,elem);
}
//some functions that create selects with managers etc
//now assume there are other selects that will filter down the teams,
//so we might have a function that creates on change events
function createOtherSelects(that){
//code that creates locations, depending on location chosen will
//filter the managers
$('#location').on('change',function(){
//do some stuff
//... then call create management
createManagement(that,elem);
});
}
function createManagement(that,elem){
var currentLocation = that.location; //works
var area = that.area;//works ... assume these are set above
//code that returns a filter and unique set of managers back
that.teamManager = [...new Set(
data.map(person=>{
if(person.area==area &&
person.currentLocation==currentLocation
){
return person;
}
})
)].filter(d=>{if(d){return d}});
if(elem.length>0){
var selectNames = ['selectManager','selectTeamManager'];
var fcns = [that.setManager,that.setTeamManager];
for(var i = 0; i < selectNames.length;i++){
//do stuff
if(certainCriteriaMet){
// filter items
if(filteredManager == 1){
fcns[i].call(null,currentManager);//
}
}
}
}
}
}
var xx = new createPerson()
In console I see setting manager and setting team manager to with the correct values.
however when I call xx in console, I see everything else set except for
xx.teamManager and xx.manager
instead it is applying to the window, so if I type teamManager in the console, it will return with the correct person.
If I straight up say
that.setManager('Steve')
or even it works just fine.
xx.setManager('steve')
the this value in setManager is somehow changing from the current instance of the object to this window. I don't know why, and I would like to learn how to use apply and call using that for future reference.
I think the issue is with your following code
fcns[i].call(null,currentManager)
If you are not supplying "this" to call, it will be replaced with global object in non-strict mode.
fcns[i].call(that,currentManager)
See mdn article here
From your codepen example, you need to change that line
fcnset[0].apply(that,[randomName]);
The first argument of the apply method is the context, if you are not giving it the context of your method it's using the global context be default. That's why you end up mutating the window object, and not the one you want !
When I am actually entering the XXXX YYYY, then I am getting the players json code in my html page (around 150 values).
But when I am trying to use a function on the players list it somewhy does not contain all the 150 values and the try throws me into the catch error part, where I can see that players json has only 100 players inside there.
Any idea what could be the problem?
if(yourID === "XXXX" && targetID === "YYYY"){
return players;
}
try{
if(isUserAlive(yourID)){
if(targetID === ""){
return userTargetInfo(yourID);
}
var checkForMatch = getUserTarget(yourID);
if(checkForMatch === targetID){
killTarget(targetID);
getUser(yourID).targetID = getTargetTarget(targetID);
addScore(yourID);
return userTargetInfo(yourID);
//return getTargetTargetStats(targetID);
}else{
return "INVALID";
}
}else{
return "DEAD"
}
}catch(err){
console.log("Error",console.log(players))
return "INVALID"
}
Edit: Since I had no time, I created 2 websites and divided the database into 2 different databases, so it would work under 100 people on each. Did not have time to fix the error at this point. So I won't be choosing the solution to that since I won't be trying that any time soon.
Thank you for all your help!
Check the link api that you are using , it might have pagination integrated with it . in that case i will return certain number of object 1st and then you can re-request to get next batch . Most likely they might have a option to change the no of object returned (sometimes with max value)
I'm pretty sure body is returned as a string. Try changing it to an object so you can work with it easier.
Change:
players = body;
to:
players = JSON.parse(body);
I'm not sure the rest of your code, but you may want to add var on your players variable declaration because this looks like the first time you are setting it.
Research: namespace collisions
If you are still having issues, edit your question to include the response you are getting from console.log(JSON.parse(body));. You will be able to get more helpful answers. Personally, I am curious to see the keys such as:
{ query:
{ count: 1,
created: '2017-04-23T22:03:31Z',
lang: 'en-US',
results: { channel: [Object] } } }
If it's paginated, you should see some kind of cursor key in there, or prev and next along with some kind of totalCount.
Hope this helps.
Hi guys so i have a form which takes in data like item name, code etc, so when i press the enter button...iv used json stringify and get an alert of what is stored in the local storage. However when i click reset and create a new item it just shows the new item, is there anyway i could store all my items created in local storage...? this is part of my coding. Im a beginner so please excuse if the question is too simple
Thanks
$('#myBox #EnterButton').click(function() {
ItemData = {
'ItemCode' : $('#ItemCode').val(),
'ItemName' : $('#ItemName').val()
};
localStorage.ItemData=JSON.stringify(ItemData);
$('#myBox').slideUp();
});
...
$('#myBox2 #EnterButton').click(function() {
if (localStorage.ItemData) {
alert(localStorage.ItemData);
ItemData = JSON.parse(localStorage.ItemData);
}
if (ItemData.ItemCode) {
$('#myBox #ItemCode').val(ItemData.ItemCode);
}
if (ItemData.ItemName) {
$('#myBox #ItemName').val(ItemData.ItemName);
}
})
and i have declared itemdata as a public variable. Hope its clear. TIA
You have to organize the way ÿou are using localStorage (a getter/setter class might help with maintenance). From your code above, it seems you are always overriding localStorage.ItemData with the values just inserted.
The solution would be for localStorage.Items to be an array and whenever you insert in that array, iterate over it to see if it's not already added (based on itemCode for instance) and override or insert a new item.
I presume this is enough for playing around. A more advanced concept than localStorage and used for slightly different purpuses would be pouchdb - websql in the browser.
Sounds like if you want multiple items to be stored, you should be using an array of itemData objects. You'll then have to parse this (array), and use something like Array.push(newItem) then stringify it again before saving it.
var allItems = localStorage.getItem('ItemData');
if(allItems == null) {
localStorage.setItem('ItemData', JSON.stringify([ItemData]));
} else {
allItems = JSON.parse(allItems);
allItems.push(ItemData);
localStorage.setItem('ItemData', JSON.stringify(allItems));
}
(there are other questions but neither helped me out)
Hi, I would like to know if this is the right way to filter the results I get from service where I'm displaying only one result (like a detail).
I know I could use ng-repeat and filter it in the view and that is the cleanest, but I want to have more control over because I will re-use some of the data in the controller for other operations.
Right now I'm doing this:
$scope.savedEvents = Event.getPayedEvents(); //gets a list from service
//Goes through entire list and checks for a match
angular.forEach($scope.savedEvents, function(event) {
if (event.IdEvent == $stateParams.eventId) {
$scope.showEvent = event;
}
});
//now if there is a match I can use $scope.showEvent.eventName etc
Not sure if this would be easier using $filter to return just one event that has correct IdEvent. Or if someone has better solution, please let me know.
thanks
I don't see any problems with what you have, but you could inject the $filter service and do this one liner:
$scope.showEvent = $filter('filter')($scope.savedEvents, { IdEvent: $stateParams.eventId });
EDIT: Here is an easy way to resolve the result to a single value from the returned array:
var showEvents = $filter('filter')($scope.savedEvents, { IdEvent: $stateParams.eventId });
$scope.showEvent = showEvents && showEvents.length ? showEvents[0] : null;
In CoffeeScript it is a little more concise:
$scope.showEvent = $filter('filter')($scope.savedEvents, { IdEvent: $stateParams.eventId })?[0]