React State Hook initialised to [] still shows type as undefined - javascript

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()

Related

How to create dynamic states and update it in react?

I have list of data getting fetched from the API and each index in data is creating a <div> which consist of some information and button along with it.
Now when I click on button, a textarea and submit button should open for that <div> and closes when clicked again.
I tried to create this here.
To achieve this, I am creating n number of states and update the state when user click the button.
How can I achieve this and where am I going wrong ?
This is a working example of what you're looking for:
const { useState } = React;
const Counter = () => {
const [count, setCount] = useState(0);
const data = [
{"id":100},
{"id":200},
{"id":300},
{"id":400},
];
const [show,setShow] = useState([]);
const handleClick = id => e => {
const showCopy = [...show];
if(show[id]){
showCopy[id] = false;
} else {
showCopy[id] = true;
}
setShow(showCopy);
}
return (
<div>
{
data.map((k,v) => (
<div key={v}>
<div>
<p>Data{v+1}</p>
<p>Some More Information</p>
</div>
<button onClick={handleClick(v)}>Update</button>
<br/>
{
show[v] ? <div>
<textarea></textarea>
<button id={v}>Delete</button>
</div> : <></>
}
<hr/>
</div>
))
}
</div>
)
}
ReactDOM.render(<Counter />, document.getElementById('app'))
A couple things were wrong in your sample code:
Improper use of dot operators instead of brace operators
The JS dot (.) operator is used to access named attributes associated with the given JS object. In these particular cases, the values v and id are dynamic, and not the true names of the attributes desired. Using the brace [] operators allows you to access the dynamic value stored in the variable.
Improper array state update
In your example, the code you had written was creating a new array based on the contents of the previous array and adding a new object every time with a literal attribute named id. In the updated code, a copy of the original state array is created and the desired boolean value with index id is modified.

Why am I getting an empty array and incorrect data fetching in console?

I have two buttons, the movies and tvshows button. When I click on either I change the option to the opposite one as shown on the handleMovieClick and handleTVShowsClick methods. Movies and TVshows may have the same id, that is why the option is important for fetching.
I am printing to the console.
(1) array of movie/tv ids
(2) the option selected
(3) each individual object fetched based on the id and option
When I use the value to calculate the array of movie ids available the first time, I first get an empty array and then the expected result. That is the array of movie ids, option is movie and each individual promise objects is fulfilled.
Here's the kicker, when I click on the tvshows button, I am getting the following two things consoled.
(1) same movie array of ids not updated, option changes to tv, however I get a bunch of promises with "status_message: "The resource you requested could not be found" because I am basically trying to retrieve promises with movie ids, but tvshow option.
Then,
(2) Then the result is changed and I everything as expected. That is option stays the same, changed in above step, the array gets updated, and the individual promise objects are all fulfilled.
What is happening?
I get that it is happening because of the code in my JSX where I am making use of the array ids. I think I need to only call the array.map() part after the useEffect has run and the so array is updated but how to do this and why is useEffect not running the first time?
Here is the code.
const Results = () => {
const options = ['movie', 'tv'];
const { value } = useParams(); // from router
const [ page, setPage ] = useState(1);
const [ option, setOption ] = useState(options[0]);
const [ array, setArray ] = useState([]);
const handleMovieClick = () => {setOption('movie')}
const handleTVShowClick = () => {setOption('tv')}
useEffect(() => {
console.log('HERE');
fetchArrayByPage(value, page, option).then(res => setArray(res));
}, [value, page, option])
console.log(array);
console.log(option);
return (
<div className="results-container">
<div>
<ul className='categories'>
<button onClick={handleMovieClick}>Movies<span>{0}</span></button>
<button onClick={handleTVShowClick}>TV Shows<span>{0}</span></button>
</ul>
</div>
{
<div>
<div className='results-list'>
{array.map((arr, index) => {
console.log(fetchID(arr, option));
return <Card key={index} id={arr} option={option}></Card>})}
</div>
<div className='pages'>
</div>
</div>
}
</div>
);
}
useEffect's callback is fired after the render and fetchArrayByPage seems to be async. So clicking on "TV shows" results in:
option is changed to "tv" (array stays the same)
console.log(array); console.log(option);
render with new option and old array
console.log('HERE'); fetchArrayByPage(...)
some time passes
setArray(res)
render with new option and new array
You should call fetchArrayByPage from your handle*Click handlers:
const handleTVShowClick = () => {
fetchArrayByPage(value, page, "tv").then(res => {
setOption('tv');
setArray(res);
});
}

How to loop through new input boxes renderer in React and get the values in an array?

I need to dynamically generate multiple divs with a single input-box in it, so the user can add a number.
The user can add by clicking a button, any number of divs with input-box to put a number in it.
After the user end with the entry of the data, must click a button to process the data.
I've find out how to use React to iterate through an existing array, but not about how to iterate through a new DOM tree that was created dynamically by the user, and then generate an array with the values inside all the input-boxes.
After processing, different values will be displayed (max value, min value, average, return results from equations, etc)
Without seeing your code it's hard to help, but you probably want to use controlled inputs rather than uncontrolled ones, and so you'd have an array of the current values for the inputs as state information.
For instance, in a functional component using hooks:
const { useState } = React;
function Example() {
// The values we use on the inputs
const [values, setValues] = useState([]);
// Update the value at the given index
const updateValue = (value, index) => {
setValues(values =>
Object.assign([], values, {[index]: value})
);
};
// Add an input
const addInput = () => {
setValues(values => [...values, ""]);
};
// Get the sum (just an example; and one of the very few places I'll use `reduce`)
const sum = values.reduce((sum, value) => sum + Number(value), 0);
// Render as many inputs as we have values, along with the
// button to add an input and the sum
return (
<div>
<div>
{values.map((value, index) =>
<div key={index}>
<input type="text" value={value} onChange={evt => updateValue(evt.target.value, index)} />
</div>
)}
</div>
<div>Sum: {sum}</div>
<input type="button" value="Add Input" onClick={addInput} />
</div>
);
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
I think you could just create one div, and an array to store the values, then create a function that everytime the user choose to add a new value, it saves the current on that array and clean the input. So, when the user select to process the data it takes that array and do what you need.

The state variable returned after using useState react hook shows .map is not a function

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

Unable to render an array of values (React component)

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: []
};

Categories