React doesn't render form autofill suggestions - javascript

I'm using react to create a panel to add a new product. I've created a separate auto-complete class which is supposed to render an input element and a list of suggested autofill items underneath. The input element shows but not the autofill suggestions. Have a look at the code
Autofill class
import React, { Component } from "react";
import firebase from "../Firebase";
export default class AutoCompleteDistID extends Component {
constructor() {
super();
this.state = {
sellerName: [],
sellerId: [],
suggestions: [],
};
}
componentDidMount() {
var sellerRef = firebase.database().ref().child("Sellers");
sellerRef.once("value", (snapshot) => {
snapshot.forEach((childSnap) => {
var distrName = childSnap.val().sellerName;
var distrId = childSnap.val().sellerName.sellerId;
// var distrName = [{ name: data.sellerName }];
this.setState((prevState) => {
return {
sellerName: [...prevState.sellerName, distrName],
sellerId: [...prevState.sellerId, distrId],
suggestions: [...prevState.suggestions, distrName],
};
});
});
});
}
onTextChange = (e) => {
var sellerNames = [this.state.sellerName];
const value = e.target.value;
let newSuggestions = [];
if (value.length > 0) {
const regex = new RegExp(`^${value}`, "i");
newSuggestions = sellerNames.sort().filter((v) => regex.test(v));
}
this.setState(() => ({ newSuggestions }));
};
renderSuggestions() {
const newSuggestions = this.state.suggestions;
if (newSuggestions.length === 0) {
return null;
}
return (
<ul>
{newSuggestions.map((item) => (
<li>{item}</li>
))}
</ul>
);
}
render() {
return (
<div>
<input onChange={this.onTextChange} />
{this.renderSuggestions}
</div>
);
}
}
Main form
import React, { Component } from "react";
import firebase from "../Firebase";
import AutoCompleteDistID from "./AutoCompleteDistID";
export default class Products extends Component {
constructor() {
super();
this.state = {
description: "",
prodQty: "",
};
this.pushProduct = this.pushProduct.bind(this);
}
handleFormChange = (event) => {
const target = event.target;
const colName = target.name;
this.setState({
[colName]: event.target.value,
});
};
pushProduct() {
const userRef = firebase.database().ref().child("Users"); //Get reference to Users DB
const prodData = this.state;
userRef.push(prodData);
}
render() {
return (
<div>
<br />
<form style={{ border: "solid", borderWidth: "1px", width: "600px" }}>
<br />
<input
type="text"
value={this.state.prodQty}
placeholder="Available Quantity"
onChange={this.handleFormChange}
name="prodQty"
/>
<input
type="text"
value={this.state.description}
placeholder="Description"
onChange={this.handleFormChange}
name="description"
/>
<AutoCompleteDistID />
<br />
<br />
</form>
<button onClick={this.pushProduct} type="button">
Add Product
</button>
<br />
</div>
);
}
}

State variable is suggestions but you are setting newSuggestions.
onTextChange = (e) => {
var sellerNames = [this.state.sellerName];
const value = e.target.value;
let newSuggestions = [];
if (value.length > 0) {
const regex = new RegExp(`^${value}`, "i");
newSuggestions = sellerNames.sort().filter((v) => regex.test(v));
}
// HERE IS THE MISTAKE
this.setState(() => ({ suggestions: newSuggestions }));
};
In AutoCompleteDistID render method
render() {
return (
<div>
<input onChange={this.onTextChange} />
{this.renderSuggestions()}
</div>
);
}

Related

clearTimeout() is affecting something completely separate in React

Upon clicking the submit button many things happen including cancelling the countdown clock. I am using setTimeout() and it actually works just fine, but for some reason I can no longer delete items. When I remove the setTimeout() deleting the items works just fine. I really have no idea what's going on as they are completely separate things and use completely separate variables.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Image } from './Image.js'
import { Button } from './Button.js'
import { images } from './assets/images.js'
import { Countdown } from './Countdown.js'
import { DisplayCount } from './DisplayCount.js'
import { Inputs } from './Inputs.js'
class Game extends React.Component{
constructor(props){
super(props)
this.timer = null
this.thoughts = []
this.thought = []
this.state = {
currentImg: 0,
timer: null,
ranNum: null,
thought: [],
isSubmitted: false
}
this.handleClick = this.handleClick.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleDeleteClick = this.handleDeleteClick.bind(this). < -------------
}
countdownClock = async (newRanNum) => {
const startingNum = newRanNum * 20;
for(let i = startingNum; i >= 0; i--) {
await new Promise(resolve => {
this.timer = setTimeout(() => {
this.setState({
timer: i
})
resolve()
}, 1000)
});
}
}
handleChange(event, index) {
const inputs = [...this.state.thought];
inputs[index] = event.target.value
this.setState({
thought: inputs
});
}
handleClick(){
clearTimeout(this.timer)
let newRanNum = Math.floor(Math.random() * 20);
this.countdownClock(newRanNum)
this.generateStateInputs(newRanNum)
// this.generateInputs(this.state.thought)
let current = this.state.currentImg;
let next = ++current % images.length;
this.setState({
currentImg: next,
ranNum: newRanNum
})
}
generateStateInputs(newRanNum){
let inputArray = []
for(let i = 0; i < newRanNum; i++){
inputArray.push('')
}
return this.setState({
thought: inputArray
});
}
handleSubmit(event) {
if(event){
clearTimeout(this.timer) <------------------------------------------
event.preventDefault();
let thought = this.state.thought.map(word => word + ' ');
console.log(thought)
this.thoughts.push(thought)
event.preventDefault()
this.setState({
thought: []
})
}
}
handleDeleteClick(index){ <-------------------------------------------------------
return this.thoughts = this.thoughts.filter(thought => thought !== this.thoughts[index])
}
render(){
let src = this.state.currentImg;
return(
<div>
<Countdown name={'Countdown: '} countdown={this.state.timer} />
<DisplayCount name='Word Count: ' count={this.state.ranNum} />
<Image src={images[src]} />
<Button onClick={this.handleClick} name='Generate Inputs' />
<form onSubmit={this.handleSubmit}>
<ol>
{this.state.thought.map((input, index) => (
<Inputs type='text' key={index} value={input} onChange={event => { this.handleChange(event, index) }} className='textInputs' />
))}
</ol>
<input type='submit' value='Submit' />
</form>
<div>
<ol>
{this.thoughts.map((thought, index )=>
<DisplayPoem key={index} onClick={() => { this.handleDeleteClick(index) }} name='Delete Thoughts' value={thought} /> <---------------------------------------------
)}
</ol>
</div>
</div>
)
}
}
class DisplayPoem extends React.Component {
render(){
return (
<li>
<span>{this.props.value}</span>
<button onClick={this.props.onClick}>{this.props.name}</button>
</li>
)
}
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);

Boolean checkbox value in JSX doesn't work

IU'm using a github code example and that works perfectly with text inputs & numbers, but when I want to use checkbox inputs, the application doesn't understand the values and returns null or just on values...
Any idea why this doesn't work on fEdit function?
import React, { Component } from "react";
// import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
title: "React Simple CRUD Application",
act: 0,
index: "",
datas: []
};
}
componentDidMount() {
this.refs.name.focus();
}
handleChange = e => {
console.log(e.target.value);
};
fSubmit = e => {
e.preventDefault();
console.log("try");
let datas = this.state.datas;
let name = this.refs.name.value;
let isenable = this.refs.isenable.value;
if (this.state.act === 0) {
//new
let data = {
name,
isenable
};
datas.push(data);
console.log(data.isenable);
} else {
//update
let index = this.state.index;
datas[index].name = name;
datas[index].isenable = isenable;
}
this.setState({
datas: datas,
act: 0
});
this.refs.myForm.reset();
this.refs.name.focus();
};
fRemove = i => {
let datas = this.state.datas;
datas.splice(i, 1);
this.setState({
datas: datas
});
this.refs.myForm.reset();
this.refs.name.focus();
};
fEdit = i => {
let data = this.state.datas[i];
this.refs.name.value = data.name;
this.refs.isenable.value = data.isenable;
this.setState({
act: 1,
index: i
});
this.refs.name.focus();
};
render() {
let datas = this.state.datas;
return (
<div className="App">
<h2>{this.state.title}</h2>
<form ref="myForm" className="myForm">
<input
type="text"
ref="name"
placeholder="your name"
className="formField"
/>
<input
type="checkbox"
ref="isenable"
placeholder="your isenable"
className="formField"
/>
<button onClick={e => this.fSubmit(e)} className="myButton">
submit
</button>
</form>
<pre>
{datas.map((data, i) => (
<li key={i} className="myList">
{data.name} - {(data.isenable || false).toString()}
<button onClick={() => this.fRemove(i)} className="myListButton">
remove
</button>
<button onClick={() => this.fEdit(i)} className="myListButton">
edit
</button>
</li>
))}
</pre>
</div>
);
}
}
export default App;
When working with checkboxes, refer to the checked property not the value. This is the true or false state of the checkbox.
In fSubmit:
let isenable = this.refs.isenable.checked;
In fEdit:
this.refs.isenable.checked = data.isenable;
In the render:
{data.name} - {data.isenable ? 'on' : 'off'}
Working demo
Complete code with fixes:
class App extends Component {
constructor(props) {
super(props);
this.state = {
title: "React Simple CRUD Application",
act: 0,
index: "",
datas: []
};
}
componentDidMount() {
this.refs.name.focus();
}
handleChange = e => {
console.log(e.target.value);
};
fSubmit = e => {
e.preventDefault();
console.log("try");
let datas = this.state.datas;
let name = this.refs.name.value;
let isenable = this.refs.isenable.checked;
if (this.state.act === 0) {
//new
let data = {
name,
isenable
};
datas.push(data);
console.log(data.isenable);
} else {
//update
let index = this.state.index;
datas[index].name = name;
datas[index].isenable = isenable;
}
this.setState({
datas: datas,
act: 0
});
this.refs.myForm.reset();
this.refs.name.focus();
};
fRemove = i => {
let datas = this.state.datas;
datas.splice(i, 1);
this.setState({
datas: datas
});
this.refs.myForm.reset();
this.refs.name.focus();
};
fEdit = i => {
let data = this.state.datas[i];
this.refs.name.value = data.name;
this.refs.isenable.checked = data.isenable;
this.setState({
act: 1,
index: i
});
this.refs.name.focus();
};
render() {
let datas = this.state.datas;
return (
<div className="App">
<h2>{this.state.title}</h2>
<form ref="myForm" className="myForm">
<input
type="text"
ref="name"
placeholder="your name"
className="formField"
/>
<input
type="checkbox"
ref="isenable"
placeholder="your isenable"
className="formField"
/>
<button onClick={e => this.fSubmit(e)} className="myButton">
submit
</button>
</form>
<pre>
{datas.map((data, i) => (
<li key={i} className="myList">
{data.name} - {data.isenable ? 'on' : 'off'}
<button onClick={() => this.fRemove(i)} className="myListButton">
remove
</button>
<button onClick={() => this.fEdit(i)} className="myListButton">
edit
</button>
</li>
))}
</pre>
</div>
);
}
}
Sidenote: I wouldn't use Refs in this case. Take a look at the docs, you can see when to use Refs and when not to. The Forms docs cover how to handle forms without Refs.
You should use the checked attribute of the isenable element to determine whether the checkbox is checked and put this value in your form. You can check the mdn docs: https://developer.mozilla.org/fr/docs/Web/HTML/Element/Input/checkbox

using component state in another component react js

Im working on a little web app, and im stuck on getting the answers data from answer class that i created.
to sum the project, i have a class createQuiz, where i handle the input for question data, and i have an answerOptions state array to collect all the answers, but i created another component answer to have the states of the answers and when i modify them, the answeroptions dont update.
Here is the code for the answer class :
import React, {Component} from 'react';
import createQuiz from './CreateQuiz'
export default class Answer extends Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.onChangeAnswerValidity = this.onChangeAnswerValidity.bind(this);
this.ChangeValidityState = this.ChangeValidityState.bind(this);
this.state = {
answerText : '',
answerValidity : false
}
}
getCurrentState(){
return (this.state)
}
handleInputChange(event) {
this.setState({
answerText : event.target.value
})
}
onChangeAnswerValidity(event){
this.setState({
answerValidity : !event.target.value
})
}
ChangeValidityState(event){
this.setState({
answerValidity : !event.target.value
})
}
render() {
return (
<div>
<input type="text"
className="form-control"
value={this.state.answerText}
onChange={this.handleInputChange}
/>
<input type="radio"
className="form-control"
value={this.state.answerValidity}
checked={this.state.answerValidity}
onChange={this.onChangeAnswerValidity}
/>
</div>
)
}
}
and here is the code for the create quiz class :
import React, {Component} from 'react';
import axios from 'axios';
import Answer from './Answer';
export default class CreateQuiz extends Component {
constructor(props) {
super(props);
this.onChangeQuestion = this.onChangeQuestion.bind(this);
this.onChangeQuestionTitle = this.onChangeQuestionTitle.bind(this);
this.onChangeAnswerOptions = this.onChangeAnswerOptions.bind(this);
this.onAddItem = this.onAddItem.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.onChangeQuestionAutor = this.onChangeQuestionAutor.bind(this);
this.onChangeValue = this.onChangeValue.bind(this);
this.onChangeAnswerValidity = this.onChangeAnswerValidity.bind(this);
this.onSubmit = this.onSubmit.bind(this);
// this.GenerateAnswers = this.GenerateAnswers.bind(this);
this.state = {
questionTitle: '',
questionAutor: '',
question: '',
answers : [],
answerOptions: [],
}
}
onChangeValue = event => {
this.setState({ currentAnswerValue: event.target.value });
};
onAddItem = () => {
// not allowed AND not working
this.setState(state => {
const newAnswer = {
answerText : '',
answerValidity : false
};
const list = this.state.answerOptions.push(newAnswer);
return {
list,
};
});
};
handleInputChange(event) {
const newIds = this.state.answerOptions.slice() //copy the array
const index = this.state.answerOptions.findIndex((el) => (el.id === event.params.id))
newIds[index].answerText = event.target.value //execute the manipulations
this.setState({answerOptions: newIds}) //set the new state
}
onChangeAnswerValidity(event){
const newIds = this.state.answerOptions.slice() //copy the array
const index = this.state.answerOptions.findIndex((el) => el.answerText === event.target.value)
newIds[index].answerValidity = event.target.value //execute the manipulations
this.setState({answerOptions: newIds}) //set the new state
}
onChangeQuestionAutor(e){
this.setState({
questionAutor: e.target.value
});
}
onChangeQuestionTitle(e) {
this.setState({
questionTitle: e.target.value
});
}
onChangeQuestion(e) {
this.setState({
question: e.target.value
});
}
onChangeAnswerOptions(e) {
this.setState({
answerOptions: e.target.value
});
}
onSubmit(e) {
e.preventDefault();
console.log(`Form submitted:`);
console.log(`questionTitle: ${this.state.questionTitle}`);
console.log(`questionAutor: ${this.state.questionAutor}`);
console.log(`question: ${this.state.question}`);
// this.GenerateAnswers();
console.log(`answerOptions: ${this.state.answers}`);
const newQuiz = {
questionTitle: this.state.questionTitle,
questionAutor: this.state.questionAutor,
question: this.state.question,
answerOptions: this.state.answers,
}
axios.post('http://localhost:4000/quizzes/add', newQuiz)
.then(res => console.log(res.data));
this.setState({
questionTitle: '',
questionAutor: '',
question: '',
answers : [],
answerOptions: []
})
}
answerList(){
return this.state.answerOptions.map(function(currentAnswer, index) {
return <Answer answer = {currentAnswer} key = {index}/>;
});
}
// GenerateAnswers(){
// const newAnswers = this.state.answers
// this.state.answerOptions.map(function(currentAnswer, index){
// const newAnswer = this.state.answerOptions[index].getCurrentState()
// newAnswers.push(newAnswer)
// });
// this.setState({answers: newAnswers});
// }
render() {
return (
<div style={{marginTop: 20}}>
<h3>Create new question</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Question Title: </label>
<input type="text"
className="form-control"
value={this.state.questionTitle}
onChange={this.onChangeQuestionTitle}
/>
</div>
<div className="form-group">
<label>Question Autor: </label>
<input type="text"
className="form-control"
value={this.state.questionAutor}
onChange={this.onChangeQuestionAutor}
/>
</div>
<div className="form-group">
<label>Question: </label>
<input type="text"
className="form-control"
value={this.state.question}
onChange={this.onChangeQuestion}
/>
</div>
<div>
<ul>
{this.answerList()}
</ul>
</div>
<div className="form-group">
<button type="button" onClick = {this.onAddItem} className="btn btn-primary"> Add answer </button>
</div>
<div className="form-group">
<input type="submit" value="submit Question" className="btn btn-primary" />
</div>
</form>
</div>
)
}
}
when i send the data to the database i always have in the answer fields empty string and false no update has been done.
thanks a lot,
Boubker ELAMRI
You’re modifying the list, so react doesn’t know it’s changed. You need to create a new array first, before setting the state
const list = Array.from(this.state.answerOptions)
list.push(newAnswer)

How to create an array based on multiple inputs

I am trying create an array with some objects in it, Im trying to gather the data from multiple inputs. I am creating a restaurant Menu, where I will have different titles such as Breakfasts, Entrees... and under each title I will have different plates.
Im trying to create an array like this:
menu: [
[ 'Lunch',
[{plate: 'Rice and Beans', description: 'Rice and Beans for Lunch', price: 50.49 }]
]
[ 'Dinner',
[{plate: 'Some Dinner', description: 'Dinner Description', price: 35.49 }]
]
]
The question is, how do I add first a Title, and under that title how do I add plates?
I also wanted to know how to make it, so I made it for practice. I hope it helps.
import React from 'react';
class MenuInput extends React.Component {
render() {
const {id, handleInput} = this.props;
return (
<div>
Title : <input name="title" onChange={(e) => handleInput(id, e)}/>
Plate : <input name="plate" onChange={(e) => handleInput(id, e)}/>
Description : <input name="description" onChange={(e) => handleInput(id, e)}/>
Price : <input name="price" onChange={(e) => handleInput(id, e)}/>
</div>
)
}
}
export default class Menu extends React.Component {
state = {
inputCount: 1,
inputData: [[]],
result: []
}
saveData = (e) => {
const {inputData, result} = this.state;
inputData.forEach(input => {
const {title, plate, description, price} = input;
const findInputIndex = result.findIndex(data => data.indexOf(title) >= 0);
if (findInputIndex >= 0) {
const [menuName, menuList] = result[findInputIndex];
result[findInputIndex] = [menuName, [...menuList, {plate, description, price}]]
} else {
result.push([title, [{plate, description, price}]])
}
});
this.setState({
result
})
}
handleInput = (id, e) => {
const {name, value} = e.target;
const {inputData} = this.state;
inputData[id] = {...inputData[id], [name]: value};
this.setState({
inputData
})
}
addInput = () => {
const {inputCount, inputData} = this.state;
this.setState({
inputCount: inputCount + 1,
inputData: [...inputData, []]
})
};
getInputList = () => {
const {inputCount} = this.state;
let inputList = [];
for (let i = 0; i < inputCount; i++) {
inputList.push(<MenuInput id={i} key={i} handleInput={this.handleInput}/>)
}
return inputList
}
render() {
const {result} = this.state;
console.log(result)
return (
<div>
{this.getInputList()}
<button onClick={this.addInput}>Add Plate</button>
<br/>
<button onClick={this.saveData}>save</button>
{
result.length > 0 && result.map(res => {
const [menuName, menuList] = res;
return (
<div key={menuName}>
<strong>Title : {menuName}</strong>
{menuList.map(menu => {
const {plate, description, price} = menu;
return(
<div key={plate}>
<span style={{marginRight : '10px'}}>plate : {plate}</span>
<span style={{marginRight : '10px'}}>description : {description}</span>
<span>price : {price}</span>
</div>
)
})}
</div>
)
})
}
</div>
)
}
}

Changing state of child component from parent with refs

I have a TaskList. I'm trying to display a child component TaskEdit (edit box for the task) several components lower, onClick of a button.
TaskEdit has a state variable hidden, set to true by default. How do I change the state inside the child component from within the child component?
React documentation mentions a forwardRef() function in order to reference the child component, but I haven't any luck so far with it.
Here's my code:
import TaskEdit from '../TaskEdit';
class TasksBase extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
tasks: [],
};
this.markTaskAsCompleted = this.markTaskAsCompleted.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.incrementDate = this.incrementDate.bind(this);
this.toggleTaskEdit = this.toggleTaskEdit.bind(this);
}
componentDidMount() {
this.onListenForTasks();
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { tasks, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{tasks ? (
<TaskList tasks={tasks}
handleToggleEdit={this.toggleTaskEdit}
handleMarkCompleted={this.markTaskAsCompleted}
handleDeleteTask={this.deleteTask}
taskEditElement={this.editTaskElement}
/>
):(
<div>There are no tasks ...</div>
)}
</div>
);
}
onListenForTasks() {
this.setState({ loading: true });
this.unsubscribe = this.props.firebase
.tasks()
.orderBy('importance', 'desc')
.orderBy('urgency', 'desc')
.orderBy('created', 'desc')
.onSnapshot(snapshot => {
if (snapshot.size) {
let tasks = [];
snapshot.forEach(doc =>
tasks.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
tasks: tasks,
loading: false
});
} else {
this.setState({ tasks: null, loading: false });
}
});
}
markTaskAsCompleted(task){
// Some code
}
deleteTask(id){
// Some code
}
toggleEditTask(){
this.editTaskElement.current.toggle();
}
}
const Tasks = withFirebase(TasksBase);
const TaskList = ({ tasks, handleToggleEdit, handleMarkCompleted, handleDeleteTask }) => (
<ul className="tasks">
{tasks.map( task => (
<Task key={task.uid}
task={task}
handleToggleEdit={handleToggleEdit}
handleMarkCompleted={handleMarkCompleted}
handleDeleteTask={handleDeleteTask}
/>
))}
</ul>
);
const Task = ({ task, handleToggleEdit, handleMarkCompleted, handleDeleteTask}) => (
(!task.completed && !task.obsolete && !task.waitingForDependencies) && (!task.start || task.start.toMillis() <= Date.now()) &&
<li className="task">
<strong>{task.userId}</strong> {task.name}
{ task.estimate &&
<span>{task.estimate.amount + task.estimate.unit}</span>
}
<div className="icons">
<FontAwesomeIcon icon="edit" onClick={() => handleToggleEdit(task)} />
<FontAwesomeIcon icon="check-circle" onClick={() => handleMarkCompleted(task)} />
<FontAwesomeIcon icon="times-circle" onClick={() => handleDeleteTask(task.uid)}/>
</div>
<TaskEdit task={task} />
</li>
);
const condition = authUser => !!authUser;
export default compose(
withEmailVerification,
withAuthorization(condition),
)(Tasks);
TaskEdit:
import React, { Component } from 'react';
import { withFirebase } from '../Firebase';
const INITIAL_STATE = {
hidden: true,
};
class TaskEditBase extends Component {
constructor(props) {
super(props);
this.state = { ...INITIAL_STATE };
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
console.log("name " + name);
console.log("value " + value);
this.setState({
[name]: value
});
}
toggle(){
this.setState({
hidden: !this.prevState.hidden
})
}
onSumbitTaskEdit = (event) => {
event.preventDefault();
};
render(){
return(
!this.state.hidden &&
<div className="task-edit">
<form onSubmit={this.onSumbitTaskEdit}>
<div>A bunch of inputs</div>
<input type="submit" value="Edit"/>
</form>
</div>
)
}
}
const TaskEdit = withFirebase(TaskEditBase);
export default TaskEdit;

Categories