can not set data from back end use React.js - reactjs

This is from back-end data format:
{data: Array(15)}
data[0]:
option: Array(1)
0: "String"
_id: "String"
col1: "String"
col2: "String"
col3: "String"
....data[14]
and this is front end code:
const Component1 = () => {
const [dbvalue, setDbvalue] = useState(null)
//option, _id, col1, col2, col3
const getAlldbValue = async () => {
try {
const resp = await services.getAlldbValue();
console.log(resp) //enter F12 can get total data from back end.
setDbvalue(resp.data.data)
console.log(dbvalue) //setDbvalue() not work , this print null
} catch (error) {
console.log(error)
alert('Failed!')
}
}
useEffect(() => {
getAlldbValue()
}, [])
if(!dbvalue) {
return (
<p>Loading Component...</p> //not appear
)
}
return (
<p>{dbvalue}</p> //not appear
);
};
export default Component1;
How can I set this nested object json into this dbvalue?
I want use dbvalue.keyname to display.
Thanks for your help

To display JavaScript objects in your jsx you can do the following:
<pre>{JSON.stringify(dbvalue,undefined,2)}</pre>
If your data[x].option is an object where every property is a string then you can do the following to list them:
const ListProps = props => (
<ul>
{Object.entries(props).map(([key, value]) => (
<li key={key}>{value}</li>
))}
</ul>
)
//here is how you would render listProps
<listProps {...data[0].option} />

First, your console.log is printing null because when you use setState, the variable you just setted will only be setted in the next render. Try printing resp.data.data instead and then put console.log(dbvalue) outside the getAlldbValue function, so you can understand better.
Also,
`return (
<p>{dbvalue}</p> //not appear
);`
your trying to put an object into a HTML Element, try dbvalue[0].col1 or whatever you want to show instead.
Hope it helps you.

Related

How can I handle the value of array or data in json file?

I'm making checkbox function using data in json file.
I want to show 'O' and 'X' according to the value called checked in json file.
And want checked value changed when 'O' or 'X' is clicked.
For this, I tried to make handleCheck function but it doesn't work.
May I know which part is wrong and how to modify it?
App.js
import "./styles.css";
import dummy from "./database/data.json";
export default function App() {
const handleCheck = (number) => {
dummy.state[number].checked !== dummy.state[number].checked;
};
return (
<div className="App">
{dummy.state.map((item, idx) => {
return (
<div
onClick={() => {
handleCheck(item.id);
}}
key={idx}
>
{item.checked ? "O" : "X"}
</div>
);
})}
</div>
);
}
data.json
{
"state": [
{
"id": "1",
"checked": true
},
{
"id": "2",
"checked": true
},
{
"id": "3",
"checked": true
}
]
}
codesandbox
https://codesandbox.io/s/serene-forest-j0cb25?file=/src/database/data.json:0-176
You're running into some JavaScript syntax issues and React-specific issues.
One of them is that you're modifying the JSON file array directly, and React isn't going to re-render on those changes. A way to fix this would be to use the useState hook with your dummy data as the initial state. You can accomplish this like so:
const [data, setData] = useState(dummy.state)
Now, we have a data variable which corresponds to your dummy data's state array and a setData function which will let us set data to whatever value we'd like and have React re-render the UI.
The next bit we need to tackle will be to make sure the handleCheck function is updating our useState's data variable and setting it correctly. We have two big issues right now with this function.
First, you're really close to flipping the checked value, but the o.checked !== o.checked is doing a comparison instead of setting your variable; basically, the code is asking "is X strictly not equal to X?" MDN has a great page on what strict inequality is. What we'd like to do is set A.checked to "not A.checked". To fix this, we can do item.checked = !item.checked.
Additionally, calling dummy.state[number].checked doesn't point to what you probably think it will since your IDs are keys on an object. If number = 1, then dummy.state[1] actually ends up being the second item in the array, because JavaScript arrays are 0-indexed. Instead, we can update the function like so
const handleCheck = (id) => {
setData(
data.map((item) => {
return item.id === id
? { ...item, checked: !item.checked }
: { ...item };
})
);
};
Now what we're doing is using the map function to loop over your data, destructure it (arrays are references, and we don't want to mutate the "old" data), and conditionally change the checked property based on if the item's ID matches the ID passed into handleChecked.
I've forked and updated your CodeSandbox so you can see it all put together in App.js.
First you need to move the json data to a state value and update the state value accordingly and at the end you can push the json data out
import "./styles.css";
import dummy from "./database/data.json";
export default function App() {
const [data,setData]=useState(dummy.state)
const handleCheck = (number) => {
setData(state=>{
let newState=[...state]
newState[number].checked = !state[number].checked;
return newState
}
};
return (
<div className="App">
{data.map((item, idx) => {
return (
<div
onClick={() => {
handleCheck(item.id);
}}
key={idx}
>
{item.checked ? "O" : "X"}
</div>
);
})}
</div>
);
}
The data state value have the correct values and you can use them accordingly

How to render custom elements for each item in an object (Map data structure) in React TS?

I've been using this method to render multiple custom elements from an array but this is my first time doing it using a map data structure. It compiles fine but renders nothing. I've set up a codesandbox here.
import "./styles.css";
import React from "react";
import ImageGrid from "./ImageGrid";
interface OnlineImage {
id: string;
url: string;
}
export default function App() {
const [onlineImages, setOnlineImages] = React.useState<Map<string, OnlineImage[]>>();
React.useEffect(() => {
let onlineImageMap = new Map<string, OnlineImage[]>();
// fake api call
onlineImageMap.set("randomImageSet1", [
{ id: "1", url: "https://picsum.photos/200" },
{ id: "2", url: "https://picsum.photos/200" }
]);
onlineImageMap.set("randomImageSet2", [
{ id: "1", url: "https://picsum.photos/200" },
{ id: "2", url: "https://picsum.photos/200" }
]);
// set state
setOnlineImages(onlineImageMap);
}, []);
return (
<div className="App">
<div>Below should render my custom ImageGrid for each item in map...</div>
<>
{onlineImages?.forEach((imageSet, category) => {
return (
<>
<div>Image Category: {category}</div>
<ImageGrid images={imageSet} />
</>
);
})}
</>
</div>
);
}
Hi Samuel: I think you should first convert the map to an Array and then use the Array.prototype.map method which actually returns a copy of the original array with your function applied to it.
As you probably already figured out the return statement does nothing within a forEach function (to be more exact it only stops the execution of the code but does not bring back anything into the outer context).
If you really want to use forEach you'll have to use an array or Object to catch it then use Object.entries/.map to iterate over it
const myMap = new Map(Object.entries({a: 1, b: 2}))
const myMapCaptureList = []
myMap.forEach((value, key) => {
myMapCaptureList.push([key, value])
}
// then you can use myMapCaptureList
myMapCaptureList.map(([key, value]) => <div>{key}: <span>{value}</span></div>);
But I would suggest that it is much easier to use the very helpful Array.from method that can help convert a Map into an array of key/value pair entries: [[key1, val1], [key2, val2]]. It is essentially equivalent to running Object.entries(someObject).
{Array.from(onlineImages || []).map(([category, imageSet]) => {
return (
<>
<div>Image Category: {category}</div>
<ImageGrid images={imageSet} />
</>
);
})}
You are using the .forEach method - which returns nothing, instead use .map that is identical but does return things
{onlineImages?.map((imageSet, category) =>
<>
<div>Image Category: {category}</div>
<ImageGrid images={imageSet} />
</>
)}

How to return a value from Firebase to a react component? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed last year.
I am trying to read a value from RealtimeDatabase on Firebase and render it in an element, but it keeps returning undefined. I have the following code:
const getStudentName = (studentId) => {
firebase.database().ref('students').child(studentId).on("value", (snapshot) => {
return snapshot.val().name;
})
}
const StudentName = (studentId) => ( <p>{getStudentName(studentId)}</p> )
I know it's nothing wrong with the database itself or the value I'm finding, because if I do:
const getStudentName = (studentId) => {
firebase.database().ref('students').child(studentId).on("value", (snapshot) => {
console.log(snapshot.val().name);
return "Test";
})
}
I still see a correct name from my database outputted to console as expected, yet "Test" is not returned to the element. However, if I do it like this:
const getStudentName = (studentId) => {
firebase.database().ref('students').child(studentId).on("value", (snapshot) => {
console.log(snapshot.val().name);
})
return "Test";
}
then "Test" is returned to the element and displayed. I'm very confused, as I don't understand how my console.log() can be reached inside the function but a 'return' statement right after it will not return.
New to React and Firebase, please help! Thank you.
EDIT: I'm sure it's self-explanatory, but you can assume a simple database in the form:
{ "students": [
"0": { "name": "David" },
"1": { "name": "Sally" } ]}
If 'studentId' is 0 then 'console.log(snapshot.val().name)' successfully outputs 'David', but 'David' will not return to the element.
You can't return something from an asynchronous call like that. If you check in the debugger or add some logging, you'll see that your outer return "Test" runs before the console.log(snapshot.val().name) is ever called.
Instead in React you'll want to use a useState hook (or setState method) to tell React about the new value, so that it can then rerender the UI.
I recommend reading the React documentation on the using the state hook, and the documentation on setState.
I'm not sure where you are consuming getStudentName, but your current code makes attaches a real-time listener to that database location. Each time the data at that location updates, your callback function gets invoked. Because of that, returning a value from such a function doesn't make much sense.
If you instead meant to fetch the name from the database just once, you can use the once() method, which returns a Promise containing the value you are looking for.
As another small optimization, if you only need the student's name, consider fetching /students/{studentId}/name instead.
const getStudentName = (studentId) => {
return firebase.database()
.ref("students")
.child(studentId)
.child("name")
.once("value")
.then(nameSnapshot => nameSnapshot.val());
}
With the above code, getStudentName(studentId) now returns a Promise<string | null>, where null would be returned when that student doesn't exist.
getStudentName(studentId)
.then(studentName => { /* ... do something ... */ })
.catch(err => { /* ... handle errors ... */ })
If instead you were filling a <Student> component, continuing to use the on snapshot listener may be the better choice:
const Student = (props) => {
const [studentInfo, setStudentInfo] = useState({ status: "loading", data: null, error: null });
useEffect(() => {
// build reference
const studentDataRef = firebase.database()
.ref("students")
.child(props.studentId)
.child("name");
// attach listener
const listener = studentDataRef.on(
'value',
(snapshot) => {
setStudentInfo({
status: "ready",
data: snapshot.val(),
error: null
});
},
(error) => {
setStudentInfo({
status: "error",
data: null,
error
});
}
);
// detach listener in unsubscribe callback
return () => studentDataRef.off(listener);
}, [props.studentId]); // <- run above code whenever props.studentId changes
// handle the different states while the data is loading
switch (studentInfo.status) {
case "loading":
return null; // hides component, could also show a placeholder/spinner
case "error":
return (
<div class="error">
Failed to retrieve data: {studentInfo.error.message}
</div>
);
}
// render data using studentInfo.data
return (
<div id={"student-" + props.studentId}>
<img src={studentInfo.data.image} />
<span>{studentInfo.data.name}</span>
</div>
);
}
Because of how often you might end up using that above useState/useEffect combo, you could rewrite it into your own useDatabaseData hook.

how to render array in object in array? (react)

const checked = [{
food:['apple', 'banana']
drink:['wine', 'beer']
}];
render (
<>
{checked.map((value) => {
value.food.forEach((each) => (
<div>{each}</div>
)
)}
</>
)
I tried this way and noting is shown in browser..
what would be the best way to approach?
Need to Return Your data like below!!
import React from "react";
export default function App() {
let checked = [{
food:['apple', 'banana'],
drink:['wine', 'beer']
}];
return (
<div className="App">
{
checked.map((item) => {
return item.food.map((fruit)=>{
return <h1>{fruit}</h1>
})
})
}
</div>
);
}
Your code has multiple errors.
It should be render instead of rander
While defining object, multiple properties should be separated using a comma. So put comma after the food array.
forEach doesn't return any thing. It just iterates over an array. So, if you want to return something (in this case a div element), use map.
Also, you should use key for each div element otherwise react would give you a warning in the console. This is done so that while re-rendering, based on the keys, react would understand which component to re-render and which to skip. Otherwise all the div would be re-rendered which is a costly operation.
const checked = [
{
food: ["apple", "banana"],
drink: ["wine", "beer"]
}
]
return (
<>
{checked.map((value) => {
return value.food.map((each, index) => {
return <div key={index}>{each}</div>;
});
})}
</>
);
There is a couple of improvements that require to be implemented to make the list displayed.
First, the map method does not return anything.
Two solutions:
Remove the curly brackets checked.map((value) => value...
Add a return keyword: checked.map((value) => { return value...}
The other issue is that the second loop is iterated using the forEach method.
The difference between the two (forEach and map) from MDN:
The forEach() method executes a provided function once for each array
element.
MDN
The map() method creates a new array populated with the results of
calling a provided function on every element in the calling array.
MDN
Basically, it means that forEach does not return anything and that why you need to use map
checked.map((value) => {
return value.food.map((each) => (<div>{each}</div>))
})}
or
checked.map((value) =>
value.food.map((each) => (<div>{each}</div>))
)}
You are iterating over the checked array items using forEach which won't induce any results since the forEach method
executes a provided function once for each array element.
which won't result in a transformed array.
What you are looking for is the map method which
creates a new array populated with the results of calling a provided function on every element in the calling array.
hence returning your transformed items so that they can be rendered (transformed at compilation time to ReactElement using the JSX syntax).
Note that you need to use an HTML tag instead of a React.Fragment the empty tag <> syntax:
const checked = [{
food:['apple', 'banana'], // there is a missing comma here
drink:['wine', 'beer']
}];
render ( // render and not rander
<div> // div instead of empty tag
{checked.map((item) => item.food.map((each) => <div>{each}</div>))}
</div>
)
Can check this approach. if you want to print just food values, below code should work. If you want to print all the values (both food and drink), then uncomment the commented code below.
export default function App() {
const checked = [
{
food: ["apple", "banana"],
drink: ["wine", "beer"]
},
{
food: ["grapes", "oranges"],
drink: ["coke", "sprite"]
}
];
// to display values of all arrays.
// return (
// <>
// {checked.map((value) => {
// const keys = Object.keys(value);
// return keys.map((eachKey) => {
// return value[eachKey].map((individualValue) =>
// (
// <div>{individualValue}</div>
// )
// )
// });
// })
// }
// </>
// );
// To display just food values.
return (
<>
{checked.map((value) => {
return value.food.map((each) => <div>{each}</div>);
})}
</>
);
}

Unable to display promise data inside a method in render

I realize I can't return objects in a react render but I'm having a problem figuring out how to do this.
Error message:
Invariant violation, objects are not valid as a React child error
My method:
displayListItemComponent = item => {
return this.getConcernsDB(item.values)
.then(returnedConcerns => {
if(returnedConcerns.length) {
returnedConcerns.forEach(el => {
return(el.name);
})
}
}
)
}
Partial render:
<CollapseBody>
{ el.details.map(item => (
this.displayListItemComponent(item)
))}
</CollapseBody>
Here I am trying to return el.name from displayListItemComponent
I am attempting to return displayListItemComponent in my react render.
My assumption was that by returning the function this.getConcernsDB that it would chain down and it would return el.name. How can I achieve this?
Your method returns a Promise. Render should be synchronous and have no side effects (e.g. it should only return allowed values from props and state). Therefore you need to store your data in state, and render elements from state
You could do something like this. Also what does el.name contain?
displayListItemComponent =(props) => {
const {item} = props;
const [data, setData] = useState();
useEffect(() => {
// i see you did this.getConcernsDB. where is it coming from?
getConcernsDB(item.values)
.then(returnedConcerns => {
setData(returnedConcerns.values(el => el.name))
)
}, [])
if(!data) return <SomeLoadingSpinner />
// now you have access to the names in `data` variable. Return whatever component you want using the `data`
}

Resources