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]);
Related
// App.js
const [currentContent, setCurrentContent] = useState('')
const openNote = (id) => {
notes.forEach(note => note.id == id && setCurrentContent(note.content))
}
Part of return:
<TextRegion className="center" content={currentContent}/>
I am passing content as a prop via a state seen in the code block above. This seems to work fine.
// TextRegion.js
import React, {useState} from 'react'
const TextRegion = ({ content }) => {
const [areaText, setAreaText] = useState(content)
console.log(areaText)
return (
<div className="form-div">
<form className="form">
<textarea className="content form-control" type="text" value={content}/>
<button style={{'float': 'right'}} className="btn btn-primary mt-2 mr-2" type='submit'>Save</button>
</form>
</div>
)
}
export default TextRegion;
The issue arises when I attempt to set the content prop to the default state of areaText. Content is of type string, and prints to console just fine. Although, when trying to print areaText, an empty string is returned. This is baffling to me, any explanations? Thanks.
The initial value for useState is used once when the component is mounted. In your TextRegion component, areaText is set to the value of the content prop when TextRegion is mounted. Since currentContent is initialised as an empty string, so is areaText.
If you change the value of content prop while the TextRegion is mounted, the useState hook controlling areaText will just ignore the new value, because it has already initialised the value for areaText.
One issue I found is in your textRegion.js you are using form and the button is type of submit so that will reload your page. always use e.preventDefault on submit method in react applications.
You can pass both currentContent and setCurrentContent via pros and use that state directly rather than creating a new state.
Use two way binding in react.
Example
<textarea
className="content form-control" type="text"
value={areaText}
onChage={(e)=> setAreaText(e.target.value)}
/>
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.
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