Why is my for loop exiting before my condition - javascript

import ReactDOM from "react-dom";
import React, { useState } from "react";
const App = () => {
let [persons, setPersons] = useState([{ id: 11, name: "Arto Hellas" }]);
const [newName, setNewName] = useState("");
const check = (arr, name) => {
let i = 0;
let checking = true;
for (i = 0; i < arr.length; i++) {
console.log(arr[i].name === name);
if (arr[i].name === name) {
checking = false;
break;
}
console.log(i);
return checking;
}
};
const addName = event => {
event.preventDefault();
const nameObject = {
id: newName.length + 1,
name: newName
};
check(persons, nameObject.name)
? setPersons((persons = persons.concat(nameObject)))
: window.alert(`${nameObject.name} is already listed`);
setNewName("");
console.log(JSON.stringify(persons));
};
const handleNameChange = event => {
setNewName(event.target.value);
};
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={addName}>
<div>
name: <input value={newName} onChange={handleNameChange} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
<ul>
{persons.map(person => (
<li key={person.id}>{person.name} </li>
))}
</ul>
</div>
);
};
export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
The problem is with my check function which checks if a name exists in the array. I have a for loop that iterates throughout the array but it always stops early. I checked this with console.log(i). If you add Adib once the array looks like this [{"id":11,"name":"Arto Hellas"},{"id":5,"name":"Adib"}] and the value of i is 0 since because before this the length of arr was 1. However if you add Adib again it will do so and value of i is 0 again and not 1

You have return checking in loop. Just put it after }:
const check = (arr, name) => {
let i = 0;
let checking = true;
for (i = 0; i < arr.length; i++) {
console.log(arr[i].name === name);
if (arr[i].name === name) {
checking = false;
break;
}
console.log(i);
}
return checking;
};
See full example in playground: https://jscomplete.com/playground/s522611

I'm sorry because I'm not on VScode right now I don't have a plugin which lets me see my brackets more clearly and accidentally had my return statement in a for loop.

Related

Each child in a list should have a unique "key" prop . Check the render method of `NewPost`

hello i am not sure why i getting this error massage can some one correct my code i can not find key in my new post components
this is my newpost.js code
import React, { useContext } from 'react';
import { useHttpClient } from '../../hooks/useHttpClient';
import useForm from '../../hooks/useForm';
import { AuthContext } from '../../context/auth';
import { useHistory } from 'react-router-dom/cjs/react-router-dom.min';
import { newPostForm } from '../../utils/formConfig';
import { appendData, renderRepeatedSkeletons } from '../../utils';
import ErrorModal from '../../components/Modal/ErrorModal';
import SkeletonElement from '../../components/Skeleton/SkeletonElement';
const NewPost = () => {
const auth = useContext(AuthContext);
const history = useHistory();
const { currentUser } = auth;
const { isLoading, sendReq, error, clearError } = useHttpClient();
const { renderFormInputs, renderFormValues, isFormValid } =
useForm(newPostForm);
const formValues = renderFormValues();
const formInputs = renderFormInputs();
const postSubmitHandle = async (evt) => {
evt.preventDefault(); //otherwise, there will be a reload
const formData = appendData(formValues);
formData.append('author', currentUser.userId);
try {
await sendReq(
`${process.env.REACT_APP_BASE_URL}/posts`,
'POST',
formData,
{
Authorization: `Bearer ${currentUser.token}`,
}
);
history.push('/');
} catch (err) {}
};
return (
<>
<ErrorModal error={error} onClose={clearError} />
{isLoading ? (
renderRepeatedSkeletons(<SkeletonElement type='text' />, 20)
) : (
<div className='container-create-page'>
<form className='form form__create'>
<h2>Create a new post</h2>
{formInputs}
<button
onClick={postSubmitHandle}
className='btn'
disabled={!isFormValid()}
>
Submit <span>→</span>
</button>
</form>
</div>
)}
</>
);
};
export default NewPost;
and this is my useform.js code
import { useState, useCallback } from 'react';
//"signupForm" => "formObj" (name, email, password) => "form"
const useForm = (formObj) => {
const [form, setForm] = useState(formObj);
const renderFormInputs = () => {
//renders an [] of <Input> for all input fields
return Object.values(form).map((inputObj) => {
const { value, label, errorMessage, valid, renderInput } = inputObj;
return renderInput(
onInputChange,
value,
valid,
errorMessage,
label,
onCustomInputChange
);
});
};
const renderFormValues = () => {
let values = {};
Object.keys(form).forEach((inputObj) => {
values[inputObj] = form[inputObj].value;
});
return values;
};
const isInputFieldValid = useCallback(
(inputField) => {
for (const rule of inputField.validationRules) {
if (!rule.validate(inputField.value, form)) {
inputField.errorMessage = rule.message;
return false;
}
}
return true;
},
[form]
);
const onInputChange = useCallback(
(event) => {
const { name, value } = event.target;
let inputObj = { ...form[name], value };
const isValidInput = isInputFieldValid(inputObj);
if (isValidInput && !inputObj.valid) {
inputObj = { ...inputObj, valid: true };
} else if (!inputObj.touched && !isValidInput && inputObj.valid) {
inputObj = { ...inputObj, valid: false };
}
inputObj = { ...inputObj, touched: true };
setForm({ ...form, [name]: inputObj });
},
[form, isInputFieldValid]
);
const onCustomInputChange = useCallback(
(type, value, InputIsValid) => {
setForm({
...form,
[type]: { ...form[type], value, valid: InputIsValid },
});
},
[form]
);
const isFormValid = useCallback(
(customForm) => {
let isValid = true;
const arr = Object.values(customForm || form);
for (let i = 0; i < arr.length; i++) {
if (!arr[i].valid) {
isValid = false;
break;
}
}
return isValid;
},
[form]
);
return {
renderFormInputs,
renderFormValues,
isFormValid,
setForm,
};
};
export default useForm;
submit button does not work . it just a simple form with submit button and 4 input value and one image . if some one need more information . please ask in the comment section
index.js contain renderRepeatedSkeletons
export const checkInArray = (arr, elem) => {
return arr && arr.indexOf(elem) !== -1;
};
export const canModifyComment = (currentUserId, authorId) =>
currentUserId === authorId;
export const canReply = (currentUserId) => !!currentUserId;
export const isReplying = (activeComment, commentId) =>
activeComment &&
activeComment.type === 'replying' &&
activeComment.id === commentId;
export const isEditing = (activeComment, commentId) =>
activeComment &&
activeComment.type === 'editing' &&
activeComment.id === commentId;
export const readingTime = (body) => {
const wpm = 225;
const words = body.trim().split(/\s+/).length;
return `${Math.ceil(words / wpm)} min read`;
};
export const appendData = (data) => {
const formData = new FormData();
for (let [key, value] of Object.entries(data)) {
if (Array.isArray(value)) {
value = JSON.stringify(value);
}
formData.append(`${key}`, value);
}
return formData;
};
export const getReplies = (comments, commentId) => {
return (
comments &&
comments
.filter((comment) => comment && comment.parentId === commentId)
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
);
};
export const formatDate = (date) => {
const options = { year: 'numeric', month: 'short', day: 'numeric' };
const today = new Date(date);
return today.toLocaleDateString('en-US', options);
};
export const getRandomColor = () => {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
export const renderRepeatedSkeletons = (element, count) => {
let skeletons = [];
for (let i = 0; i < count; i++) {
skeletons.push(element);
}
return skeletons;
};
export const renderAlternateSkeletons = (elementOne, elementTwo, count) => {
let skeletons = [];
for (let i = 0; i < count; i++) {
if (i % 2 === 0) {
skeletons.push(elementOne);
} else {
skeletons.push(elementTwo);
}
}
return skeletons;
};
Your renderRepeatedSkeletons function have to add a key to elements
export const renderRepeatedSkeletons = (element, count) => {
let skeletons = [];
for (let i = 0; i < count; i++) {
skeletons.push(<React.Fragment key={i} />{element}</React.Fragment>);
}
return skeletons;
};

react - not able to setValue for input box

I am making 2 otp input in my application.
In Input.tsx, I am using react-otp-input for the otp functionality
if
<OtpInput
value={"abcde"}
...
numInputs={5}
/>
The UI of react-otp-input will be
Now the problem is, when I try to change the value of otp, it throws error
Cannot set properties of undefined (setting 'value')
How can I fix it?
Input.tsx
import React, { useState } from "react";
import OtpInput from "react-otp-input";
type InputPropType = {
value: string;
setValue: (event: string) => void;
};
function Input(props: InputPropType): JSX.Element {
const { value, setValue } = props;
return (
<OtpInput
value={value}
onChange={(e: string) => {
setValue(e);
}}
numInputs={5}
/>
);
}
export default Input;
App.tsx
import React, { useState } from "react";
import Input from "./Input";
export default function App() {
type InputValueType = {
id: number;
value: string;
};
const [inputValues, setInputValues] = useState<Array<InputValueType>>([
{ id: 0, value: "" },
{ id: 1, value: "" }
]);
const InputGroup = () => {
let numOfInputs: number = 2;
var rows: Array<any> = [];
for (var i = 0; i < numOfInputs; i++) {
let inputValue: InputValueType = inputValues[i];
rows.push(
<Input
key={inputValue.id}
value={inputValue.value}
setValue={(event: string) => {
let inputValuesTemp = inputValues;
inputValuesTemp[i]["value"] = event;
setInputValues(inputValuesTemp);
}}
/>
);
}
return <>{rows}</>;
};
return (
<div className="App">
<InputGroup />
</div>
);
}
Codesandbox
https://codesandbox.io/s/react-typescript-forked-s38ck9?file=/src/App.tsx:0-918
Few things to be corrected,
There is an issue with closures in your for-loop since you have used var instead of let. All the i variables refer to the same for-loop closure in the for-loop, which means i is 2 at the end of the iteration.
All the inputValuesTemp[i] are now resolved to inputValuesTemp[2] which is definitely undefined.
Replace var with let to create closures for each iteration of the loop.
for (let i = 0; i < numOfInputs; i++) {...}
And, you also need o get a copy of the values array for react to do a rerender (to inform that the array has changed).
let inputValuesTemp = [...inputValues];
Your InputGroup component was within the App component, which caused you to lose focus for each keystroke. To fix the focus issue, move the InputGroup out of the App and keep it as a separate component.
import React, { useState } from "react";
import Input from "./Input";
type InputValueType = {
id: number;
value: string;
};
const InputGroup = () => {
const [inputValues, setInputValues] = useState<Array<InputValueType>>([
{ id: 0, value: "" },
{ id: 1, value: "" }
]);
let numOfInputs: number = 2;
var rows: Array<any> = [];
for (let i = 0; i < numOfInputs; i++) {
let inputValue: InputValueType = inputValues[i];
rows.push(
<Input
key={inputValue.id}
value={inputValue.value}
setValue={(event: string) => {
let inputValuesTemp = [...inputValues];
inputValuesTemp[i]["value"] = event;
setInputValues(inputValuesTemp);
}}
/>
);
}
return <>{rows}</>;
};
export default function App() {
return (
<div className="App">
<InputGroup />
</div>
);
}

for-loop in function call with state hooks - React

I want my function to roll 5 dices and store them in my dices state.
In the recent version, i am only storing one object for the next click, but i made the for loop to generate 5 dices at once, how can i store the previous dce object(s) for the next iteration in the for loop?
import './App.css';
import Subgroup from './Subgroup'
import React, {useState} from 'react'
function App() {
const [numberRolls, setNumberRolls] = useState(3);
const [dices, setDices] = useState([]);
return (
<div className="App">
<Subgroup
numberRolls = {numberRolls}
setNumberRolls = {setNumberRolls}
dices = {dices}
setDices = {setDices}
/>
</div>
);
}
export default App;
import React from 'react'
const Subgroup = ({numberRolls, setNumberRolls, dices, setDices}) => {
const rollTheDice = () => {
if(numberRolls > 0) {
setNumberRolls(numberRolls - 1);
for(let i = 0; i < 5; i++) {
setDices([...dices, // i think sth should be different here????
{id: Math.random()*100,
number: Math.ceil(Math.random()*6)-1,
selected: false}
])
}
}
console.log(dices)
}
return (
<div>
<button onClick={ rollTheDice }>blablabala </button>
</div>
)
}
export default Subgroup;
Try saving the result in an array, then set the state, something like:
const result = [];
for(let i = 0; i < 5; i++) {
result.push({
id: Math.random() * 100,
number: Math.ceil(Math.random() * 6) -1,
selected: false
});
}
setDices(prev => [...prev, ...result]);

Array not getting cleared to null or empty in setState on click in react

Array not getting cleared to null or empty in setState on click in react.
When I click on the submit button, the array must be set to []. It is setting to [], but on change the previous array of items comes into the array.
let questions = [];
let qns = [];
class App extends Component {
constructor(props) {
super(props);
this.state = {
btnDisabled: true,
//questions: [],
};
}
changeRadioHandler = (event, j) => {
this.setState({ checked: true });
const qn = event.target.name;
const id = event.target.value;
let idVal = this.props.dat.mat.opts;
let text = this.props.dat.mat.opt;
let userAnswer = [];
for (let j = 0; j < text.length; j++) {
userAnswer.push(false);
}
const option = text.map((t, index) => ({
text: t.text,
userAnswer: userAnswer[index],
}));
const elIndex = option.findIndex((element) => element.text === id);
const options = { ...option };
options[elIndex] = {
...options[elIndex],
userAnswer: true,
};
const question = {
options,
id: event.target.value,
qn,
};
questions[j] = options;
qns = questions.filter((e) => {
return e != null;
});
console.log(qns, qns.length);
this.setState({ qns });
if (qns.length === idVal.length) {
this.setState({
btnDisabled: false,
});
}
};
submitHandler = () => {
console.log(this.state.qns, this.state.questions);
this.setState({ qns: [] }, () =>
console.log(this.state.qns, this.state.questions)
);
};
render() {
return (
<div class="matrix-bd">
{this.props.dat.mat && (
<div class="grid">
{this.props.dat.mat.opts.map((questions, j) => {
return (
<div class="rows" key={j}>
<div class="cell main">{questions.text}</div>
{this.props.dat.mat.opt.map((element, i) => {
return (
<div class="cell" key={i}>
<input
type="radio"
id={j + i}
name={questions.text}
value={element.text}
onChange={(event) =>
this.changeRadioHandler(event, j)
}
></input>
<label htmlFor={j + i}>{element.text}</label>
</div>
);
})}
</div>
);
})}
</div>
)}
<div>
<button
type="button"
class="btn btn-primary"
disabled={this.state.btnDisabled}
onClick={this.submitHandler}
>
SUBMIT
</button>
</div>
</div>
);
}
}
export default App;
On button click submit, the array must be set to [] and when on change, the value must be set to the emptied array with respect to its index.
changeRadioHandler = (event, j) => {
// the better way is use local variable
let questions = [];
let qns = [];
...
}
submitHandler = () => {
console.log(this.state.qns, this.state.questions);
this.setState({ qns: [] }, () =>
console.log(this.state.qns, this.state.questions)
)}
// clear the previous `qns`
// if u use local variable. you don't need those lines
// this.qns = []
// this.questions = []
}
Finally, found out the solution.
After adding componentDidMount and setting questions variable to null solved my issue.
componentDidMount = () => {
questions = [];
};
Thanks all for your efforts and responses!

Code Randomly Changing a State Element to 1

Working on a project that will allow you to search the Spotify database for songs, add it to a playlist, then add the playlist to your account, I run into an error when attempting to add a song to a playlist. Upon searching through the code to find and reason, and adding console logs to see where it goes wrong. It seems that in the render statement, the state element playlistTracks is changed from an Array with the added song inside it as an object to just the integer 1. This image shows the progression of console logs:
This is the code from App.js:
import React from 'react';
import './App.css';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
import Spotify from '../../util/Spotify.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
playlistName: 'New Playlist',
playlistTracks: []
}
this.addTrack = this.addTrack.bind(this);
this.removeTrack = this.removeTrack.bind(this);
this.updatePlaylistName = this.updatePlaylistName.bind(this);
this.savePlaylist = this.savePlaylist.bind(this);
this.search = this.search.bind(this);
}
addTrack(track) {
console.log(track);
if(this.state.playlistTracks.length !== 0) {
const ids = Playlist.collectIds(this.state.playlistTracks);
let newId = true;
for(let i = 0; i < ids.length; i++) {
if(ids[i] === track.id) {
newId = false;
}
}
if(newId) {
this.setState({playlistTracks: this.state.playlistTracks.push(track)});
}
} else {
this.setState({playlistTracks: this.state.playlistTracks.push(track)});
}
console.log(this.state.playlistTracks);
}
removeTrack(track) {
const ids = Playlist.collectIds(this.state.playlistTracks);
let trackIndex = -1;
for(let i = 0; i < ids.length; i++) {
if (ids[i] === track.id) {
trackIndex = i;
}
}
if (trackIndex !== -1) {
const newPlaylist = this.state.playlistTracks.splice(trackIndex, 1);
this.setState({playlistTracks: newPlaylist});
}
}
updatePlaylistName(name) {
this.setState({playlistName: name});
}
savePlaylist() {
let trackURIs = [];
for(let i = 0; i < this.state.playlistTracks.length; i++) {
trackURIs.push(this.state.playlistTracks[i].uri);
}
Spotify.savePlaylist(this.state.playlistName, trackURIs);
this.setState({playlistName: 'New Playlist', playlistTracks: []});
}
async search(term) {
const results = await Spotify.search(term);
this.setState({searchResults: results});
}
render() {
return (
<div id="root">
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar onSearch={this.search} />
<div className="App-playlist">
{console.log(this.state.playlistTracks)}
<SearchResults searchResults={this.state.searchResults} onAdd={this.addTrack} />
<Playlist
playlistName={this.state.playlistName}
playlistTracks={this.state.playlistTracks}
onRemove={this.removeTrack}
onNameChange={this.updatePlaylistName}
onSave={this.savePlaylist}
/>
</div>
</div>
</div>
);
}
}
export default App;
The error is likely a result of pushing and mutating state in one line.
Try:
// Add Track.
addTrack = (track) => this.setState({playlistTracks: [...this.state.playlistTracks, track]})
Array.prototype.push returns the new number of elements in the array. It does not return the array itself. You are setting the state to the returned value, which is 1.
Here are some examples to illustrate what's happening:
What you expect to happen:
let someArray = [];
someArray.push('something');
console.log(someArray); // -> ['something']
But what you are actually doing is akin to:
let someArray = [];
someArray = someArray.push('something');
console.log(someArray); // -> 1

Categories