so i'm developing a personality quiz app using one of the tutorials i found on the internet //mitchgavan.com/react-quiz/, I have a quizQuestions.js file for api where i fetch the answer and the question from, like so
{
question: "I am task oriented in order to achieve certain goals",
answers: [
{
type: "Brown",
content: "Hell Ya!"
},
{
type: " ",
content: "Nah"
}
]
},
it has type and content, so this is the initial state of the app, every time the user click on Hell YA button it will increment that type +1, for example Brown: 1 etc.. but the problem is, when user select Nah it will give me this :null , I have a AnswerOption.js component like so
function AnswerOption(props) {
return (
<AnswerOptionLi>
<Input
checked={props.answerType === props.answer}
id={props.answerType}
value={props.answerType}
disabled={props.answer}
onChange={props.onAnswerSelected}
/>
<Label className="radioCustomLabel" htmlFor={props.answerType}>
{props.answerContent}
</Label>
</AnswerOptionLi>
);
}
AnswerOption.PropTypes = {
answerType: PropTypes.string.isRequired,
answerContent: PropTypes.string.isRequired,
answer: PropTypes.string.isRequired,
onAnswerSelected: PropTypes.func.isRequired
};
and my setUserAnswer function like so
setUserAnswer(answer) {
const updatedAnswersCount = update(this.state.answersCount, {
[answer]: {$apply: (currentValue) => currentValue + 1}
});
this.setState({
answersCount: updatedAnswersCount,
answer: answer
});
}
my question is, how can i let react ignore that white space, so when user click Nah it will not do anything with it, and if there is different approach to the problem i will be gladly accept it, thanks in advance.
Simple solution to your problem is to check if answer is empty :
if(answer.trim()) {
const updatedAnswersCount = update(this.state.answersCount, {
[answer]: {$apply: (currentValue) => currentValue + 1}
});
this.setState({
answersCount: updatedAnswersCount,
answer: answer
});
}
Related
I create a discord bot that when a user enters the /play command displays a modal (thanks to discord-modals).
On this one there is a question with necessarily an input to answer it.
If the answer is wrong the interaction returns a message (it works perfectly).
If the answer is correct, a second modal is displayed directly with another question.
However, it is impossible to display the second modal if the answer to the first question is correct.
So here is my code:
// Importing Modals and Text Inputs
const { Modal, showModal, TextInputComponent } = require("discord-modals");
module.exports = {
command: "play",
name: "Play",
description: "Start the game",
category: "event",
slash: true,
callback: async ({ user, interaction, client }) => {
// The list of questions and answers that will be displayed in the modals
questions = { [0]: "What is the name of the event ?", [1]: "How are you", [2]: "Who am I ?", [3]: "What's your country ?", [4]: "What's our group's name ?", [5]: "5 + 5 = ?", [6]: "3 + 3 = ?" };
answers = { [0]: "joker event", [1]: "fine", [2]: "joker", [3]: "france", [4]: "partouche", [5]: "10", [6]: "6" };
// A simple function that will display the questions randomly
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
var rand1 = getRandomInt(5);
var rand2 = getRandomInt(5);
// My two modals
const modal = new Modal()
.setCustomId(`modal_1`)
.setTitle(`Question 1`)
.addComponents(new TextInputComponent().setCustomId(`input_1`).setLabel(`${questions[rand1]}`).setStyle("SHORT").setPlaceholder("Write your answer").setRequired(true));
const modal2 = new Modal()
.setCustomId(`modal_2`)
.setTitle(`Question 2`)
.addComponents(new TextInputComponent().setCustomId(`input_2`).setLabel(`${questions[rand2]}`).setStyle("SHORT").setPlaceholder("Write your answer").setRequired(true));
// A function that displays the modal and when answering, compares the answer entered by the user with the answer to the question
function showModals(actualModal) {
showModal(actualModal, {
client: client,
interaction: interaction,
});
client.on("modalSubmit", async (modal_answered) => {
if (modal_answered.customId === `modal_1`) {
var answer = modal_answered.getTextInputValue(`input_1`);
if (answer.toLowerCase() == answers[rand1].toLowerCase()) {
await modal_answered.reply({
content: showModals(modal2),
ephemeral: true,
});
} else {
return await modal_answered.reply({
content: `Sorry ${interaction.user}, you have answered incorrectly.`,
ephemeral: true,
});
}
}
if (modal_answered.customId === `modal_2`) {
var answer = modal_answered.getTextInputValue(`input_2`);
if (answer.toLowerCase() == answers[rand2].toLowerCase()) {
await modal_answered.reply({
content: 'You won !',
ephemeral: true,
});
} else {
return await modal_answered.reply({
content: `Sorry ${interaction.user}, you have answered incorrectly.`,
ephemeral: true,
});
}
}
});
}
showModals(modal);
},
};
I then have two errors that I can't correct:
An error occurred when showing a modal. DiscordAPIError: Interaction has already been acknowledged.
and
Uncaught DiscordAPIError DiscordAPIError: Cannot send an empty message
Thank you for your help, if you have any questions don't hesitate
I'm using Inquirer.js to create a CLI's prompter which allows users to enter/reply to some input/questions. In the last question, I want to add a feature that if the user replies no to Are you done? question, then the prompter will restart asking the questions until the user replies yes. I'm almost there with the functionality.
It's working, but only on the first time when I enter no. The second time I enter no, the prompter stops.
How can I run this on a loop to accomplish the desired behavior? What I'm doing wrong?
This is what I have some far:
import inquirer from 'inquirer';
inquirer
.prompt([
// { bunch of other questions previously },
{
type: 'confirm',
name: 'repeat_questions',
message: 'Are you done?',
},
])
.then((answers) => {
if (answers.repeat_questions) {
return inquirer.prompt([
// { bunch of other questions previously },
{
type: 'confirm',
name: 'repeat_questions',
message: 'Are you done?',
},
]);
}
})
.catch((error) => {
if (error.isTtyError) {
throw new Error(`Prompt couldn't be render in current environment`);
}
});
One way is a recursive function:
import inquirer from "inquirer";
const questions = [
{
type: "number",
name: "children_count",
message: "How many children do you have?",
},
{
type: "input",
name: "first_child_name",
message: "What is the eldest child's name?",
},
{
type: "confirm",
name: "is_finished",
message: "Are you done?",
},
];
function getAnswers() {
return inquirer.prompt(questions).then((answers) => {
if (answers.is_finished) {
return answers;
} else {
return getAnswers();
}
});
}
getAnswers()
.then(console.log)
.catch((error) => {});
The variable repeat_questions doesn't make sense, if the user says no to if they are done, repeat_questions is also no. So instead I renamed it to is_finished.
I'm trying to think of a better way to do my onChange validation for my form but it's really laggy because of the multiple rerenders.
This is my useEffect code:
useEffect(() => {
if (passwordValues.password) {
setValidPassword({ confirmPassword: validateConfirmPassword(correctPassword), password: validatePassword(correctPassword) })
}
if(formData.name){
setValidFormData(validFormData => ({...validFormData, name: validateData(correctFormData, "name")}))
}
if(formData.lastName){
setValidFormData(validFormData => ({...validFormData, lastName: validateData(correctFormData, "lastName")}))
}
if(formData.email){
setValidFormData(validFormData => ({...validFormData, email: validateData(correctFormData, "email")}))
}
if(formData.phone){
setValidFormData(validFormData => ({...validFormData, phone: validateData(correctFormData, "phone")}))
}
}, [passwordValues, correctPassword, correctFormData, formData])
I know I can maybe do that in just a couple lines but is that what is doing so many rerenders?
My formData, passwordValues, correctPassword and correctFormData change on every input change.
-- EDIT --
I removed most of the dependencies in the array and I just stayed with [formData], improves the speed, but still quite a bit laggy.
I am writing it here, because it will be a bit long explanation, not suitable for comments.
These scenarios are pretty common in any application , where you have to react to changes in values and to tackle this I just created one special hook, use-effect-x (Writing tests is pending)
This custom hook will tell you the changed item set, which can be very useful here. Below is the code , that I was able to write based on your inputs. you can make use of useEffectX as a replacement of useEffect everywhere. This way non-needed setValidFormData will not run. Give it a try and let me know.
import { useEffectX } from "use-effect-x";
useEffectX(
({
changedItem: [
changeObjConfirmPassword,
changeObjPassword,
changeObjName,
changeObjLastname,
changeObjEmail,
changeObjPhone,
],
}) => {
if (changeObjConfirmPassword.changed) {
setValidPassword({
confirmPassword: validateConfirmPassword(confirmPassword),
});
}
if (changeObjPassword.changed) {
setValidPassword({
password: validatePassword(correctPassword),
});
}
if (changeObjName.changed) {
setValidFormData((validFormData) => ({
...validFormData,
name: validateData(correctFormData, "name"),
}));
}
if (changeObjLastname.changed) {
setValidFormData((validFormData) => ({
...validFormData,
lastName: validateData(correctFormData, "lastName"),
}));
}
if (changeObjEmail.changedd) {
setValidFormData((validFormData) => ({
...validFormData,
email: validateData(correctFormData, "email"),
}));
}
if (changeObjPhone.changed) {
setValidFormData((validFormData) => ({
...validFormData,
phone: validateData(correctFormData, "phone"),
}));
}
},
[confirmPassword, password, name, lastName, email, phone]
);
Thanks and let me know, if this is not the suggestion you were expecting, I will move it.
const employeeQuestion = [
{
type: "list",
name: "employeeTitle",
message: "What's the employee's title",
choices: ["Engineer", "Intern"]
},
//when role is engineer is true, ask this question
{
when: input => {
return input.role == "Engineer"
},
type: "input",
name: "github",
message: "Enter your github username:",
},
//when role is intern is true, ask this question
{
when: input => {
return input.role == "Intern"
},
type: "input",
name: "school",
message: "What's the school you enrolled in ?",
},
]
The idea is I want to utilize inquirer's when method so the question whether to ask about the user's github or school is dependent on the answer on the employee's title. But when i run node on the command line, the question asking for github / school never appeared. I wonder if I used the method wrong or if there are other alternatives.
inquirer's when method is definitely the correct one for this situation!
There are two possible reasons:
You are using input.role but never defining a role value in the answers hash. You need to refer to the name value from the earlier question, in this case, input.employeeTitle.
If reason 1 does not fix it, try expanding your function a bit. when needs to take in the answers hash as an input, then apply the conditional to that and explicitly return a Boolean. Ex.
{
type: "input",
name: "github",
message: "Enter your github username:",
when: (answers) => {
if (answers.employeeTitle === "Engineer") {
return true;
}
}
this is my first post there :)
So How to manage array saved in reactjs state?
Context: a web site I react with, which provides multiple-choice questions to students and is managed by the teacher, an academic case
{
"id": 0,
"textvalue": "Les licornes existent-elles ?",
"singlecorrectanswer": true,
"explication": "hyhy-bèikb_b",
"answer": [
{
"id": 1,
"author": 1,
"textanswer": "Evidemment, oui",
"correct": true
},
{
"id": 2,
"author": 1,
"textanswer": "Bien sur que non",
"correct": false
}
]
}
Currently, I have this as an attribute of an Input for the question value: this.state.question.text value
But I can't modify the field by entering text, maybe because onChange is not defined.
In addition, I want the user to be able to modify each answer.
For the answer it is the same problem :
I will realize a map on my "answers", a solution is to create an onChange function that will deal with the index of the map and the array of the state and modify it. But this solution is a bit ugly. Do you have a better solution to automatically bind the "value" of the field to the state?
My apologies for my English, I'm french
Thanks
You should really have provided code we could review.
Yes, your issue is likely caused because you did not provide an onChange event.
This is in the docs https://reactjs.org/docs/forms.html#controlled-components
I tested this here:
https://codesandbox.io/s/nervous-ives-ccy4u
I found a solution, but it's not the best. As jasdhfiu said I had to provide a onChange event
Here is my code
handleQuestionChange = (field, value) => {
let question = Object.assign({}, this.state.question);
question[field] = value;
this.setState({question}, () => this.updateError());
};
handleQuestionThemeChange = (value) => {
let question = Object.assign({}, this.state.question);
question.theme = this.state.themes.find(o => o.label === value);
this.setState({question}, () => this.updateError());
};
handleAnswerChange = (field, value, index) => {
let question = Object.assign({}, this.state.question);
question.answer[index][field] = value;
this.setState({question}, () => this.updateError());
};
And my event which are place on buttons :
onChange: (e) => this.handleQuestionChange('textvalue', e.target.value)
onChange: (e) => this.handleQuestionChange('explication', e.target.value)
onClick={() => this.handleAnswerChange('correct', !answer.correct, index)
onChange: e => this.handleAnswerChange('textanswer', e.target.value, index)
onChange={e => this.handleQuestionThemeChange(e.target.value)}
If there is a simpler way let me know
Thanks
I would suggest you to use onBlur instead of onChange so that you don't need to update state on every character change.
To use onBlur you need to use defaultValue instead of value.
<input defaultValue={this.state.question.text value} onBlur={e => updateValue(e.target.value) /* your update function */} type='text' />