I am working on a quiz application and would like to enable my users to go back and forth over the questions and see what they have already selected in the previous questions.
I am storing all of the data about the questions and the answers that they selected in the state.
However, I am not able to render the selected checkboxes as they should be.
if I put a variable in the checked field just like the one below (checkbox) then all checkboxes within the question are affected and I only want to check just the selected ones.
here is example code
https://codesandbox.io/s/confident-night-zyy2h?file=/src/App.js
import React, { useState } from "react";
import "./styles.css";
const answersArray = ["answer1", "answer2", "answer3", "answer4"];
const selectedAnswers = ["answer1", "answer3"]
export default function App() {
const [checkbox, setCheckbox] = useState(false);
function handleCheckbox() {}
return (
<div className="App">
<h1>question 1 - how to manipulate the checkboxes</h1>
{answersArray.map((possibleAnswer, index) => (
<li key={[index, possibleAnswer]}>
<div>
<input
name={[index, possibleAnswer]}
type="checkbox"
onChange={(event) => handleCheckbox(event, index)}
checked={checkbox}
/>
</div>
<div>
<span>{possibleAnswer}</span>
</div>
</li>
)
)}
</div>
);
}
Any ideas on how to go about this problem? I would like to render answer 1 and 3 for example as selected and make sure my users can also unselect them and change them if they wanted.
I am not quite sure what is the exact question, but I would like to provide you my answer. Since each checkbox has an individual state of being checked or unchecked, one state field does not solve your issue, cause using one state field for all of them defines their checked/unchecked state together. This is why you had the problem of them being checked/unchecked at the same time.
The approach to solve this would be to create individual state fields for each checkbox, but obviously that is not the smartest solution. Therefore, I would recommend you to create a separate checkbox function, which has a state field inside of it, making it individual for the checkbox itself. In my code provided below, there is an ESLint error, which would be solved if you created a separate file and moved the function there.
Additionally, if you wanted to add or remove items from the selectedAnswers array, you could do so in the onChange function. Hope, this answers your question.
import React, { useState } from "react";
import "./styles.css";
const answersArray = ["answer1", "answer2", "answer3", "answer4"];
const selectedAnswers = [];
export default function App() {
const checkboxComponent = (index, possibleAnswer) => {
const [checked, setChecked] = useState(false);
return (
<>
<div>
<input
name={[index, possibleAnswer]}
type="checkbox"
onChange={() => setChecked(!checked)}
checked={checked}
/>
</div>
<div>
<span>{possibleAnswer}</span>
</div>
</>
);
};
return (
<div className="App">
<h1>question 1 - how to manipulate the checkboxes</h1>
{answersArray.map((possibleAnswer, index) => (
<li key={[index, possibleAnswer]}>
{checkboxComponent(index, possibleAnswer)}
</li>
))}
</div>
);
}
You could do this on your input checked attribute:
checked={selectedAnswers.includes(possibleAnswer)
This will be true if the possibleAnswer is included inside selectedAnswers array.
Related
-Hello, I made a code to send props value from child component to parent component
in the child component I have multiple checkboxes with a value that I'll pass to the props, in addition of two functions, the first function to store the clicked inputs on array and pass it to the props and the second function to call the first function if the user check new input (this function used to update the props value )
export const ColorsForm = (props) => {
useEffect(()=>{
test()
},[])
// this function to store the checked colors in array then pass this array to the props
const getColors = () => {
const inpColor = document.querySelectorAll(".colorsInp");
let arrOfColors = [];
for(let i = 0; i < inpColor.length; i++){
if(inpColor[i].checked){
arrOfColors.push(inpColor[i].value);
}
}
props.array_of_colors(arrOfColors);
return arrOfColors;
}
// this function will be used to resend the prop value with new value
const whenNewColorClicked = () => {
const inpColor = document.querySelectorAll(".colorsInp");
for(let i = 0; i < inpColor.length; i++){
inpColor[i].addEventListener("click",()=>{
getColors();
})
}
}
return (
<div>
<label>Colors:</label>
<div>
<input type="checkbox" id="colorBlack" value="black" name='colorsInput' />
<label htmlFor="colorBlack" className='lblBlack'></label>
<input type="checkbox" id="colorWhite" value="white" name='colorsInput' />
<label htmlFor="colorWhite" className='lblWhite'></label>
<input type="checkbox" id="colorRed" value="red" name='colorsInput' />
<label htmlFor="colorRed" className='lblRed'></label>
<input type="checkbox" id="colorBlue" value="blue" name='colorsInput'/>
<label htmlFor="colorBlue" className='lblBlue'></label>
</div>
</div>
)
}
the parent component is where I'll valid the upcoming props to make a post request
import React, { useState ,useEffect } from 'react'
import { ColorsForm } from './colorsForm';
export const AddForm = () => {
const [colors, setColors] = useState([])
return (
<>
<ColorsForm array_of_colors={res => setColors(() => [...res])} />
<button onClick={postRequestFunction} >Insert</button>
</>
);
}
my code works fine but...
I know the child component code can be wrote better than I did, please feel free to notice anything, I'll be thankful
You do an "anti-pattern" here.
The parent must have the information and the children must display and interact with this information.
So the colors array must be define in the parent and your ColorsForm must .map the colors array props to display the different color in the form with the correct value.
Then, if you use document.querySelector is that there is a problem.
React manages the DOM and you should never do it for it. In your two functions, you should never listen to the click event by hand.
So I can't explain everything here, but I hope this information will help you improve your code. The best way to learn is to search and find answers by yourself
I don't really have much experience with React but I was trying to work on a small project for myself.
So I have this dropdown list, which has 2 values, and I'm trying to console.log the current state of camera which should hold the values for the list option that you click on.
So let's say I click on an option for the 1st time, I get a log of an empty value (the initial state of my camera), then I click on the other option and I get logged the option that I clicked the 1st time. From what I understand this might be due to the fact that setState is async ?
Even so, what should I do to fix my problem, so that when I click for the first time on an option it logs the actual option ? I'm thinking of using useEffect someway but I can't really figure out how to use it for handling events.
This is my code:
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom';
import style from './Join.module.css';
const Join = () => {
const [nume, setNume] = useState('')
const [camera, setCamera] = useState('')
return (
<div className={style.mainJoinContainer}>
<div className={style.secondaryJoinContainer}>
<h1 className={style.header}>
Conecteaza-te!
<div>
<input placeholder="Nume" className={style.joinInput} type="text" onChange={(event) => { setNume(event.target.value); console.log(nume) }} />
</div>
<div className={style.margin}>
<label>Camera</label>
<select name="Camere" placeholder="Camere" onChange={(event) => { setCamera(event.target.value); console.log(camera) }}>
<option value="Camere" style={{ display: "none" }}>Camere</option>
<option value="Gluma">Gluma</option>
<option value="Joke">Jokes</option>
</select>
</div>
<Link to="/interaction/chat">
<button className={`${style.joinButton} ${style.margin}`} type="submit">Logheaza-te</button>
</Link>
</h1>
</div>
</div >
)
}
export default Join
I will appreciate any sort of guidance for this problem. I've tried looking up some similar situation and tried useEffect or async-await for some reason, but I can't really figure it out myself. Thank you for your time!
You can use useEffect to "watch" a state variable.
useEffect(() => {
// do stuff
console.log(camera);
}, [camera]);
So first I import really simple "database" object from a different file which has a students array property in it. After that I map through the students array to display all of the students on the window object. All went well until I tried to dynamically change class name on one of the div tags inside of map function every time when state for status is changing to true I want to display information otherwise that div should stay invisible. After implementing the code below and trying to tick the box I am getting error "Cannot create property '0' on boolean 'true'". Please tell me what I am doing wrong. Thank You for Your interest in that case.
import React,{ useState,useEffect } from 'react'
import db from './db'
import './App.css'
const App = () => {
const [status,setStatus] = useState(database.map((student) => {
return student.isChecked
}))
return db.students.map(({name,id,email,isChecked}, index) => {
return (
<form key={id} className='form'>
<h2>
{name}
</h2>
<select name='student_attendance' className='form__dropdown' defaultValue='present'>
<option value='present'>
present
</option>
<option value='absent'>absent</option>
</select>
<div>
<label htmlFor={id}>Left early</label>
<input type='checkbox' name='leftEarly' id={id} />
</div>
<div>
<label htmlFor={email}>Arrival late</label>
<input type='checkbox' name='arrivalLate' id={email} onChange= {(event) => {
setStatus(status =>
status[index] = event.currentTarget.checked
)
}}/>
</div>
<div className={status[index] ? 'visible' : 'invisible'>
<p>time: </p>
</div>
</form>
)
})
}
export default App
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
The argument to setStatus should be either:
The new value for status
or
A function that takes a previous version of status and returns the new version
You are passing it a function that modifies the received value of status, and then returns a boolean value. So it appears that status is changing to that boolean value, and then the status[index] in your className expression is essentially evaluating true[0], which results in the error.
To fix this, fix your use of the setStatus() function:
setStatus(status => [
...status.slice(0, index),
event.currentTarget.checked,
...status.slice(index + 1)
])
You may also want to try this, which doesn't rely on the event properties:
setStatus(status => [
...status.slice(0, index),
!status[index],
...status.slice(index + 1)
])
Or even this:
setStatus(status =>
status.map((x, i) => (i === index) ? !x : x)
)
Fails to run first if condition.
So once component is loaded it shows "Student Progress related Stats" and when once topic-link is set(in some other component which is removed too properly) it shows second display. Everything works fine till now but when topic-link is removed if fails to run first if condition and shows second display still. Basically I want to change my component view based on topic-link is there or not.
import React, { Component } from "react";
import {connect} from 'react-redux';
import {getQuestionsList} from '../../store/actions/questionActions';
class Test extends Component {
render(){
let display;
let topicLink = localStorage.getItem('topic-link');
if(!topicLink){
display =
<div style={{textAlign:'center',
fontSize:'22px'}}>
<p>Student Progress related Stats</p>
</div>
}
else if(topicLink){
display =
this.props.questions.map(question => (
<div key={question.id} style={{border:'1px solid #000',marginBottom:'15px'}}>
<div dangerouslySetInnerHTML={{__html: question.direction}} />
<div dangerouslySetInnerHTML={{__html: question.question}} />
<div>
<form>
<input type="radio" name="option" value="(A)"/>{question.option_a}<br/>
<input type="radio" name="option" value="(B)"/>{question.option_b}<br/>
<input type="radio" name="option" value="(C)"/>{question.option_c}<br/>
<input type="radio" name="option" value="(D)"/>{question.option_d}
</form>
</div>
</div>
))
}
return (
<div>
{display}
</div>
);
}
};
const mapStateToProps = state => {
return {
questions: state.questions.items,
}
}
export default connect(mapStateToProps, {getQuestionsList})(Test);
Here where i set the localStorage. On Every onTopicClick I get the desired output(meaning second if statement runs and it updates the component). Only when localStorage is removed it stays on the second if statement which not be the case(it should run first if statement). I have two fucntions(in seperate components) like onSectionClick and onTopicClick like this:
onTopicClick = () => {
this.props.getQuestionsList(this.props.topicId);
let topicName = this.props.name;
topicName = topicName.replace(/\s+/g, '-').toLowerCase();
let topicLink = "/updates/daily-practice-questions/" + this.props.sectionName + "/" + topicName;
this.props.history.push(topicLink);
localStorage.setItem('topic-link', topicLink);
}
onSectionClick = () => {
this.setState(prevState => ({
isOpened: !prevState.isOpened
}));
let sectionName = this.props.name;
sectionName = sectionName.replace(/\s+/g, '-').toLowerCase();
this.setState({sectionName: sectionName});
this.props.history.push("/updates/daily-practice-questions/" + sectionName);
if(this.state.isOpened){
this.props.history.push("/updates/daily-practice-questions");
localStorage.removeItem('topic-link')
}
}
One of the hardest topics in data communication probably.
let topicLink = localStorage.getItem('topic-link'); This line is the money line. Basically, you already identified the driver of this component, for instance, topicLink.
The next thing is to make sure this component use this variable as a prop input. I noticed you are using redux already, therefore this variable could be part of the state variables there.
Next step will be to see if your store can update this variable from localStorage. This probably is another topic, for instance, check the localStorage every 5 seconds, and then update the store variable.
However, it's not a good idea to sync components via a localStorage variable, instead, you should bootstrap your code and save this localStorage variable as a state variable first and store in your redux for example.
So either way, input prop is the first step to go, it'll help you test the code as well.
I'm moving an old multiple choice quiz app from Blaze to React.
The app grabs quizzes from the DB and then for each question it loops through the answers and prints a checkbox for each one.
Each of these answers for each of the questions was inside a form, and when the form was submitted I used jQuery to grab the ID of each :checked checkbox. These IDs were then pushed to an array and sent to the server to compare vs the correct answers.
Now that I'm using React, I'm having some difficulty replicating this functionality as using checkboxes isn't the same.
What would be the best way to get the value of the checked checkboxes in to an array?
Here is my code with as much irrelevant data cut out as possible:
Assessment.jsx
class Assessment extends React.Component {
constructor(props) {
super(props);
}
render() {
const { module } = this.props.params;
const { loading, modules, assessment } = this.props;
return (
<div className="Assessment">
<div className="section">
<form onSubmit={ this.submitForm }>
{ assessment.questions.map((question) => {
return <AssessmentQuestion question={question} key={question.number}/>
})}
<button className="btn btn-action btn-block" type="submit">Submit Assessment</button>
</form>
</div>
</div>
)
}
}
AssessmentQuestion.jsx
class AssessmentQuestion extends React.Component {
constructor(props) {
super(props);
}
render() {
const { question } = this.props;
return (
<div className="question" data-number={question.number}>
<p>
{question.number}) {question.question}
</p>
{ question.answers.map((answer) => {
return <div className="checkbox">
<label>
<input type="checkbox" name={question.number} value={answer.letter} id={answer.number, answer.letter}/>
{ answer.answer }
</label>
</div>/>
})}
</div>
)
}
}
As you can see, I am looping through each question and for each question looping through each answer and printing a checkbox. When the user submits the form in the parent 'Assessment.jsx' component, I want to collect the id of each checked checkbox and push that in to an array so I can send to the server for grading.
Any help much appreciated
There are a couple of ways you could solve this. The easier way, which is closer to the solution you had before, is to use React refs.
https://facebook.github.io/react/docs/refs-and-the-dom.html
You could add a ref to the checkboxes, and then filter down to the checked ones.
An alternative (and more "Reacty" approach) would be to manage all this "checked" logic with React state. Basically, your Assesment.jsx class would keep track of the state and then send it to the form on submit.
You create a function to update the state with the questionName and answer, and pass that function as an onClick callback to your AssesmentQuestion class.
Each checkbox would have onclik which would update state, or even better if using redux, dispatch action which would result in state update.
When sending to server only information in state would get used