I want to make a Quiz App.
Questions are checked after user presses Submit button.
So questions are checked all at once in the end.
I need to save user choices (answers) for each question and then later check them on submit.
I was thinking about this:
let [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
let [userAnswers, setUserAnswers] = useState([] as any);
function answerClick(value: any, key: any) {
// console.log('answer clicked');
userAnswers[currentQuestionIndex][key] = value;
// TypeError: undefined is not an object (evaluating 'userAnswers[currentQuestionIndex][key] = value')
}
My checkbox for each possible answer in the question:
<BouncyCheckbox
key={index}
onPress={(value) => {
answerClick(value, index);
}}
text={answer.title}
/>
But I get
TypeError: undefined is not an object (evaluating 'userAnswers[current][key] = value')
on Answer click
What is setUserAnswers() equivalent of userAnswers[currentQuestionIndex][key] = value?
Why am I getting undefined error?
How to do this, please help my head hurts.
You are attempting to access a non existing index in the array.
userAnsqers = [];
currentQuestionIndex = 0;
// userAnswers[currentQuestionIndex] === undefined
What are you attempting to do is adding an {} to the array lazily (only when needed). This will work:
function answerClick(value: any, key: any) {
setUserAnswers(latest => {
const out = [...latest];
while (out.length <= currentQuestionIndex) out.push({});
out[currentQuestionIndex][key] = value;
return out;
})
}
There is nothing equivalent to setUserAnswers() for userAnswers[currentQuestionIndex][key] = value (see). You may find som custom hooks to manage arrays or build your own.
First off, the userAnswers should have the key and values of everything you're trying to change. The reason you're getting undefined is because you're trying to target an object in the userAnswers array which can't be found. Also, you are modifying the userAnswers state directly and that's really wrong. That's what the setUserAnswers is for. You can do this instead.
function answerClick(value: any, key: any) {
// console.log('answer clicked');
setUserAnswers([
...usersAnswers,
{[key]: [value]}
]);
}
Related
I am trying to find an item from a collection, from the code below, in order to update my react component, the propertState object isnt empty, it contains a list which i have console logged, however I seem to get an underfined object when i console log the value returned from my findProperty function... I am trying update my localState with that value so that my component can render the right data.
const PropertyComponent = () => {
const { propertyId } = useParams();
const propertyState: IPropertiesState = useSelector(
propertiesStateSelector
);
const[property, setProperty] = useState()
const findProperty = (propertyId, properties) => {
let propertyReturn;
for (var i=0; i < properties.length; i++) {
if (properties[i].propertyId === propertyId) {
propertyToReturn = properties[i];
break;
}
}
setProperty(propertyReturn)
return propertyReturn;
}
const foundProperty = findProperty(propertyId, propertyState.properties);
return (<>{property.propertyName}</>)
}
export default PropertyComponent
There are a few things that you shall consider when you are finding data and updating states based on external sources of data --useParams--
I will try to explain the solution by dividing your code in small pieces
const PropertyComponent = () => {
const { propertyId } = useParams();
Piece A: Consider that useParams is a hook connected to the router, that means that you component might be reactive and will change every time that a param changes in the URL. Your param might be undefined or an string depending if the param is present in your URL
const propertyState: IPropertiesState = useSelector(
propertiesStateSelector
);
Piece B: useSelector is other property that will make your component reactive to changes related to that selector. Your selector might return undefined or something based on your selection logic.
const[property, setProperty] = useState()
Piece C: Your state that starts as undefined in the first render.
So far we have just discovered 3 pieces of code that might start as undefined or not.
const findProperty = (propertyId, properties) => {
let propertyReturn;
for (var i=0; i < properties.length; i++) {
if (properties[i].propertyId === propertyId) {
propertyToReturn = properties[i];
break;
}
}
setProperty(propertyReturn)
return propertyReturn;
}
const foundProperty = findProperty(propertyId, propertyState.properties);
Piece D: Here is where more problems start appearing, you are telling your code that in every render a function findProperty will be created and inside of it you are calling the setter of your state --setProperty--, generating an internal dependency.
I would suggest to think about the actions that you want to do in simple steps and then you can understand where each piece of code belongs to where.
Let's subdivide this last piece of code --Piece D-- but in steps, you want to:
Find something.
The find should happen if you have an array where to find and a property.
With the result I want to notify my component that something was found.
Step 1 and 2 can happen in a function defined outside of your component:
const findProperty = (propertyId, properties) => properties.find((property) => property.propertyId === propertyId)
NOTE: I took the liberty of modify your code by simplifying a little
bit your find function.
Now we need to do the most important step, make your component react at the right time
const findProperty = (propertyId, properties) => properties.find((property) => property.propertyId === propertyId)
const PropertyComponent = () => {
const { propertyId } = useParams();
const propertyState: IPropertiesState = useSelector(
propertiesStateSelector
);
const[property, setProperty] = useState({ propertyName: '' }); // I suggest to add default values to have more predictable returns in your component
/**
* Here is where the magic begins and we try to mix all of our values in a consistent way (thinking on the previous pieces and the potential "undefined" values) We need to tell react "do something when the data is ready", for that reason we will use an effect
*/
useEffect(() => {
// This effect will run every time that the dependencies --second argument-- changes, then you react afterwards.
if(propertyId, propertyState.properties) {
const propertyFound = findProperty(propertyId, propertyState.properties);
if(propertyFound){ // Only if we have a result we will update our state.
setProperty(propertyFound);
}
}
}, [propertyId, propertyState.properties])
return (<>{property.propertyName}</>)
}
export default PropertyComponent
I think that in this way your intention might be more direct, but for sure there are other ways to do this. Depending of your intentions your code should be different, for instance I have a question:
What is it the purpose of this component? If its just for getting the property you could do a derived state, a little bit more complex selector. E.G.
function propertySelectorById(id) {
return function(store) {
const allProperties = propertiesStateSelector(store);
const foundProperty = findProperty(id, allProperties);
if( foundProperty ) {
return foundProperty;
} else {
return null; // Or empty object, up to you
}
}
}
Then you can use it in any component that uses the useParam, or just create a simple hook. E.G.
function usePropertySelectorHook() {
const { propertyId } = useParams();
const property = useSelector(propertySelectorById(propertyId));
return property;
}
And afterwards you can use this in any component
functon AnyComponent() {
const property = usePropertySelectorHook();
return <div> Magic {property}</div>
}
NOTE: I didn't test all the code, I wrote it directly in the comment but I think that should work.
Like this I think that there are even more ways to solve this, but its enough for now, hope that this helped you.
do you try this:
const found = propertyState.properties.find(element => element.propertyId === propertyId);
setProperty(found);
instead of all function findProperty
Trying to set this variable to an element's id number, but I can't make sense of this error statement.
{Uncaught TypeError: setting getter-only property "selectedProjectId"}
I declared the variable here.
const LOCAL_STORAGE_LIST_KEY = 'project.lists';
const LOCAL_STORAGE_SELECTED_LIST_ID_KEY = 'project.selectedProjectId';
const projects = JSON.parse(localStorage.getItem(LOCAL_STORAGE_LIST_KEY)) || [];
const selectedProjectId = localStorage.getItem(LOCAL_STORAGE_SELECTED_LIST_ID_KEY);
function save() {
localStorage.setItem(LOCAL_STORAGE_LIST_KEY, JSON.stringify(projects));
localStorage.setItem(LOCAL_STORAGE_SELECTED_LIST_ID_KEY, selectedProjectId);
}
export {
projects,
save,
selectedProjectId,
}
I try to assign it a different value here.
projectList.addEventListener('click', e => {
if (e.target.tagName.toLowerCase() === 'li') {
console.log(e.target.dataset.projectId);
console.log('works');
selectedProjectId = e.target.dataset.projectId;
saveAndRender();
}
});
I'm following webdev simplified's tutorial on a better to-do-list in js. I'm following pretty closely, but mine keeps throwing the type error. How do I fix this? Any advice would be helpful. For more clarification, I am also using webpack if that is important.
const cannot be assigned to a new value. Use let instead.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
I am working through a little practice assignment and have come across this question and for the life of me can't figure it out.
There are tests parameters that I can't see. The object is not a variable I can see, it's just assumed.
Write a function called removePassword that takes in an object.
Delete the property password and return the object.
removePassword=(object)=>{
for(var key in object){
if(object = object[key]){
delete object[key]
}
}return object;
}
I have tried a bunch of different versions of this code, but I don't know how to just delete the property password only without deleting the other property which is a username
Take a look at this solution. You can avoid doing the object copy if you want, it'll work anyway
const removePassword = (user) => {
const userCopy = {...user} // CREATE A COPY OF THE OBJECT
delete userCopy.password // DELETE THE PASSWORD PROPERTY
return userCopy // RETURN UPDATED USER
}
const testUser = {username: 'Mario', password: 'supersecret123'}
console.log(
removePassword(testUser)
)
Could it work for you?
removePassword = (object) => {
delete object.password;
return object;
}
You can see here link
That you can do it simply delete object.password or delete object["password"] :
const removePassword = (object) => {
delete object.password;
return object;
}
I am trying to assign a variable with the properties of an object from an array in my redux state. I am trying to loop through the array of objects and assign the variable when the ID of the item matches up to the ID that I am searching for.
I have been trying anything I can from nested if statements, multiple returns, I cannot seem to figure this out.
Currently, this is what I have.
const currItemProps = () => {
this.props.todos.find((todo) => {
(todo.id === this.props.itemID) ?
{ todo } : null
}
);
};
todos is my array I am searching for and the itemID is the ID I am lookking for (both are pieces of redux state).
I am trying to open a modal on the press of a todo that has the properties of the todo. Hence I am trying to assign a variable in my modal file with all of the properties of its current todo (the object from the array).
The find function expects that you'll return True when you find your item.
also, you need to specify a 'return' statement.
const currItemProps = () => {
return this.props.todos.find((todo) => todo.id === this.props.itemID);
};
In case you directly want to return model
const currItemProps = () => {
this.props.todos.find((todo) => {
(todo.id === this.props.itemID) ?
<ComponentName todoProps={todo}/> : null
}
);
};
then you can use the same in render method like {currentProps}
I want to build a table in Raect with a sorted list of watches of a certain ebay listing.I figured out that the problem is this line:
entriesObj[value][1][0].listingInfo[0].watchCount[0]
because sometimes listing don't have any watches at all and in this case value watchCount doesn't exist at all so I can't loop through it, although I tried to use conditional operator (and if else statements in many different ways) it still throws an error. First I created an object:
watcherCount = () => {
return (
this.state.itemList.reduce((watcherObject,item) => {
const watcherKey = item.itemId;
if (!watcherObject[watcherKey]) {
watcherObject[watcherKey] = [item];
} else {
watcherObject[watcherKey].push(item);
}
return watcherObject;
},{})
);
}
and now I am trying to move them to an array ([number of watches, title of listing, item id]) in order to sort them:
import React from 'react';
class Watches extends React.Component {
render () {
var entriesObj = Object.entries(this.props.watcherCount);
var sortable = [];
for (var value in entriesObj){
for (var value in entriesObj){
sortable.push([typeof entriesObj[value][1][0].listingInfo[0].watchCount[0] === "undefined" ? "-" : entriesObj[value][1][0].listingInfo[0].watchCount[0], entriesObj[value][1][0].title[0], entriesObj[value][0]]);
}
}
sortable.sort(function(a, b) {
return b[0] - a[0];
});
console.log(sortable);
//Output: Uncaught (in promise) TypeError: Cannot read property '0' of undefined
return <table></table>
}
}
export default Watches;
Do you know any other way to build this exact kind of array or how to solve the problem with missing property?
I Don't know if I fully understood the problem.
In cases with deep references, if I don't want or can't use any conditional checks I simply put the object path reference in a try catch (finally) block.
e.g. (untested though)
for (var value in entriesObj){
var val;
try {
// just make sure the only error that might be occuring
// here is an object reference error
// therefore the array push happens after the try catch block
val = entriesObj[value][1][0].listingInfo[0].watchCount[0], entriesObj[value][1][0].title[0], entriesObj[value][0]];
} catch(err) {
val = "-";
// log the error or maybe issue a warning since missing items
// is actually expected behaviour
} finally {
sortable.push(val);
}
}
Maybe it solves your problem.