I am building a test app to learn more about React and I have made an API call which gets a huge JSON object.
I was able to break this json into the parts that I need and now I have 10 arrays of 3 props each. I am able to send these 10 arrays in 3 props to another component, which needs to use these 3 props 10 times and render a div class Card each.
I can console.log(this.props) and it shows 10 different arrays with 3 props each,however, I cannot produce a same element 10 times.. I tried using map() but since my array is initially undefined, map() is not able to function properly either. Is there any thing in react like *ngFor in Angular ?
What is the best way to go about this?
*EDIT
Here's more code guys. Sorry still noobie here..
ERROR : this.props.map is not a function
return(
<div>
{this.props.map((data,i)=>{
return(
<li key={i}>{data.likes}</li>
);
*EDIT 2
Soo I tried running map function with an if condition but the code still breaks the very moment the condition gets true..
render() {
if(this.props.url !== undefined){
this.props.map((data,i) =>{
return <li key={i}>{data.likes}</li>
})
}
My state method is :
state = {
userId: undefined,
likes: undefined,
url: undefined
}
and im setting my values on each data stream as follows :
const pics = await fetch(`${base_url}?key=${api_key}&q=${query}
&img_type=photo&per_page=12`).then(response => {
return response.json();
})
pics.hits.map((data) =>{
return this.setState({
userId: data.user_id,
likes: data.likes,
url: data.webformatURL
})
})
this.props won't have map, it's not an array. It's an object with a property for each property passed to your component. For instance:
<YourComponent foo="bar"/>
...will have this.props.foo with the value "bar".
So if you're passing an array to your component, like this:
<YourComponent theArrayProperty={[{likes: 42},{likes:27}]} />
...then you need the name of that property:
return (
<div>
{this.props.theArrayProperty.map((data,i) => {
return (
<li key={i}>{data.likes}</li>
);
})}
</div>
);
Side note: You can use a concise arrow function for the map callback instead:
return (
<div>
{this.props.theArrayProperty.map((data,i) => <li key={i}>{data.likes}</li>)}
</div>
);
...and no need for the () if you put the opening tag on the line with return (you can't leave off the ( if it's on the next line, but you probably knew that):
return <div>
{this.props.theArrayProperty.map((data,i) => <li key={i}>{data.likes}</li>)}
</div>;
...but that's a matter of style.
With little information that you have provided, my guess is that code fails at map() when you try to use it with undefined value.
Try adding a conditional check to render
{props && props.map([RENDER CODE HERE])}
You can just make simple if statement to check if the array is not undefined, and then pass it to map function.
Another option is to set a defaultProps for an empty array.
MyComponent.defaultProps = {
arrProp: []
};
Related
Code:
const [input, setInput] = useState('');
const [studentList, setStudentList] = useState([]);
useEffect(() => {
console.log(studentList)
console.log(studentList.type)
}, [studentList]);
return (
<div id="add-students-input-div">
<input
type="text"
id='add-students-input'
value={input}
placeholder='Enter a student to the database'
onChange={(event) => {
setInput(event.target.value)
}}
/>
<div id="add-students-button" onClick={() => {
setStudentList([document.getElementById('add-students-input').value, ...studentList])
setInput('')
}}>
<p>Add</p>
</div>
</div>
)
Problem:
The print statement for studentList is returning the array but the print statement for studentList.type is undefined at all times, even after elements are added to the array.
How can I ensure that studentList.type returns Array.
studentList in your code will ever be an array, empty when you initialize the state. If you want to check if there is something in the array you have to use studentList.length
Altough previous contributors solved your problem by eliminating it in other place, I would like to answer this:
How can I ensure that studentList.type returns Array
If you want to make sure your variable is an array, you may use isArray() static method of Array:
Array.isArray(studentList) // returns true if array or false if not
As mentioned in the comments, arrays do not have a type property.
Your studentList state value is always an array; there is no need to check its type.
One thing you do appear to be doing incorrectly is updating studentList when you click your button (<div>). In short, you really shouldn't need to use DOM methods in React.
To update your array with the value from your input state, use this...
const handleClick = () => {
setStudentList((prev) => [input, ...prev]);
setInput("");
};
and
<div id="add-students-button" onClick={handleClick}>
<p>Add</p>
</div>
See useState - Functional updates for information on how I'm using setStudentList()
This code gives me error when I try to map the state variable returned after calling useEffect hook.
On console, when I print the myAppointment it shows two values one is empty ([]) and other (Array[25]) I want to extract the petname values from this array . But getting error "map is not a function" Please look into it and help me!
function App() {
const [myAppointment, setmyAppointment] = useState([])
useEffect(() => {
fetch('./data.json')
.then(response=> response.json())
.then(result=>{
const apts=result.map(item=>{
return item
})
setmyAppointment(state=>({...state,
myAppointment:apts
}))
})**strong text**
},[])
console.log(myAppointment)
const listItem = myAppointment.map((item)=>{
return(
<div>{item.petName}</div>
)
})
return (
<main className="page bg-white" id="petratings">
<div className="container">
<div className="row">
<div className="col-md-12 bg-white">
<div className="container">
{/* {listItem} */}
<div><AddAppointment /></div>
<div><SearchAppointment /></div>
<div><ListAppointment /></div>
</div>
</div>
</div>
</div>
</main>
);
}
You have you state as an array. However when you try to update it you convert it into an object with key myAppointment which is the cause of your error.
You must update your state like
setmyAppointment(state=> [...state, ...apts]);
However since the state is initially empty and you only update it once in useEffect on initialLoad, you could also update it like
setmyAppointment(apts);
The problem is from the way you're setting your state after the data is fetched.
You're setting the state as an object by instead of an array.
Let's Take a look at the line where you set the state.
setmyAppointment(state=>({...state,
myAppointment:apts
}))
Using the spread operator with curly braces tells JavaScript compiler to spread an object, and obviously that's not what you want.
What you ought to do is instead of curly braces, use square brackets. That will tell the compiler you want to spread an array.
So it should be something like this .
setmyAppointment(state=>([...state
]))
And if I were you, I will do this a little bit different.
I will set the state like this
setmyAppointment(apt)
I hope this helps
I have a React Native form that allows me to add an Input UI in the form, by clicking a button with this function. This allow me to generate it on the fly. The code for that is this.
addClick() {
this.setState(prevState => ({ values: [...prevState.values, ""] }));
console.log(this.values[0].name);
}
That part works well, but I'm having a problem extracting the data from the dynamic inputs, and add it to an array. So far I have tried this
setVal = value => {
const values = this.state.values[0];
if (values[0].name === "" || values[0].description === "") return;
[...this.state.values, value];
this.setState(values);
console.log(values);
};
How do I organize my states properly so that I can add as many inputs I need, and when I'm finished, I can update the state, and access the new data in my list component?
How do I update my state to the new Array? at the moment, this.state only shows the initial state set at the top.
I'm missing a few things
Please take a look at the full code sandbox HERE so you can see:
See...your created isssue is not so obvious we need to see where you call setVal() function but....
i think you will be more comfortable if you render your <input/> s directly from your state array, not from const x = [] variant. because it seems like you want a dynamic view and in such a cases you will need to bind your loop quantity from state. so:
this.state = {
x: [0,1,2,3,4]
}
and inside your render :
{this.state.x.map(x => {
return (
<TextInput
placeholder={`name${x}`}
value={values[x.toString()]}
handleBlur={() => handleBlur(x.toString())}
onChangeText={handleChange(x.toString())}
style={styles.input}
/>
);
})}
I am using React-Redux and have problem to parse array.
I using selector to return data to component, but I am getting error TypeError: this.props.messages.map is not a function. But if I console.log values it returns me normal array. What I am doing wrong?
Components render method:
render() {
const {loading, error, messages} = this.props;
const mes = JSON.stringify(messages);
console.log(mes); //<------------- returns [{"key":"value"}]
return (
<div>
<MessageList
messages={mes}
/>
);
}
MessageList component map function:
{this.props.messages.map((item, i) => (
<MessageItem
key={i}
message={item.message}
}
Maybe someone could tell me what I am doing wrong?
What you can do is
render() {
const {loading, error, messages} = this.props;
return (
<div>
<MessageList
messages={messages}
/>
</div>
);
}
Looks like the messages are from redux state. So initially the messages props will be undefined. So before doing map check whether messages is not undefined using conditional operator like below
{this.props.messages && this.props.messages.map((item, i) => (
<MessageItem
key={i}
message={item.message}
/>
))}
In a comment you've said:
messages returns this: List, not array
JavaScript doesn't have a List type, and it's clear from the result of JSON.stringify that messages is an array, or at least something that JSON.stringify thinks is an array.
I think if you look more carefully, you'll find that messages is an array and you can just use it in your MessageList directly.
If messages really, really doesn't have a map method, you can convert it from whatever it is into an array using Array.from:
mes = Array.from(messages);
...or possibly (depending on what it is) spread notation:
mes = [...messages];
You certainly don't want JSON.stringify, which creates a string.
So I have this loop in my render:
{this.props.OneTestArrayProp.questions.map((q, i) =>
<div key={i} className="questions_div">
<div>Question: {q.question}</div>
<div>
)}
The this.props.OneTestArrayProp is loaded this way:
componentWillMount() {
if ( ! this.props.OneTestArrayProp.length ) {
this.props.dispatch(fetchOneTest(test_id));
}
}
But I'm getting:
Error: "this.props.OneTestArrayProp.questions.map" undefined
the error appears just for a second and then it disappears when Redux load the data. Of course the root of the problem is that half second when this.props.OneTestArrayProp is populated by the middleware thunk.
There is a way to indicate to React to "wait" before it does the render?
SOLVED
The solution provided by Red Mercury works pretty well, however I coded a more "redux-like" approach adding a new Array:
this.props.QuestionsArrayProp = [];
and linking it with its reducer:
QuestionsArrayProp: state.rootReducer.questions_reducer.QuestionsTestArrayProp
That way I get an initial, empty but existent array.
Check if it exists before mapping over its questions
{this.props.OneTestArrayProp &&
this.props.OneTestArrayProp.questions.map((q, i) =>
<div key={i} className="questions_div">
<div>Question: {q.question}</div>
<div>
)}
If this.props.OneTestArrayProp is undefined the expression will short-circuit and the map will never be executed