Everything is okay until end of the first step but when i write the "tempPhone" numbers for confirm
[First Step][1]
input area seems like that (input field must be empty after clicked first button) and i didnt figure out that problem. why thats happening?
[1]: https://i.stack.imgur.com/qXYB3.png
[2]: https://i.stack.imgur.com/wRc4W.png
[Problem][2]
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor() {
super();
this.state= {
otpContent: "",
input: "",
tempPhone:"123",
tempPin: "123456",
errorMsg: ""
}
this.handleChange = this.handleChange.bind(this);
this.handlePhoneSubmit = this.handlePhoneSubmit.bind(this);
this.handlePinSubmit = this.handlePinSubmit.bind(this);
}
handleChange(e) {
this.setState({[e.target.name]: e.target.value});
}
phoneInput() {
this.setState(
{
otpContent: <div>
<input type="text" name="input" onChange={this.handleChange}/>
<button onClick={this.handlePhoneSubmit}> Dogrula!</button>
</div>
}
);
}
handlePhoneSubmit() {
if(this.state.input === this.state.tempPhone){
this.setState(
{
input: ''
}
);
this.pinInput();
}
else {
this.setState({
errorMsg: "wrong phone"
});
}
}
pinInput() {
this.setState(
{
input: '',
otpContent: (<div>
<input
type="text" name="input" onChange={this.handleChange}/>
<button onClick={this.handlePinSubmit}> Pin Dogrula!</button>
</div>)
}
);
}
handlePinSubmit() {
if(this.state.input === this.state.tempPin){
this.setSuccess();
}
else {
this.setState({
errorMsg: "wrong pin"
});
}
}
setSuccess() {
this.setState(
{
otpContent: (<div>
<h3>Success!</h3>
</div>)
}
);
}
componentDidMount() {
this.phoneInput();
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Hi</h1>
</header>
<div className="App-intro">
{this.state.otpContent}
{this.state.errorMsg}
</div>
</div>
);
}
}
export default App;
Your handling input logic is a little bit flawed. You are trying to use a controlled input element but at the same time you need to set a value to it that I think which is a bad mix. I am giving an example but I've changed the component logic too much. If it does not suit you just examine this answer as an example.
I don't hold any JSX in the state since I've never seen it (but who knows?), I've changed some rendering content and message logic. I've also use class-fields and arrow functions, so no need to bind them to this.
class App extends React.Component {
state = {
input: "",
tempPhone: "123",
tempPin: "123456",
msg: "",
confirm: false,
}
handleChange = e => this.setState({ [e.target.name]: e.target.value });
handlePhoneSubmit = () => {
if (this.state.input === this.state.tempPhone) {
this.setState({confirm: true, input:""});
}
else {
this.setState({
msg: "wrong phone"
});
}
}
handlePinSubmit = () => {
if (this.state.input === this.state.tempPin) {
this.setState({msg: "Success"});
}
else {
this.setState({
msg: "wrong pin"
});
}
}
phoneInput = () => (
<div>
<input type="text" value={this.state.input} name="input" onChange={this.handleChange} />
<button onClick={this.handlePhoneSubmit}> Dogrula!</button>
</div>
);
pinInput = () => (
<div>
<input
type="text" value={this.state.input} name="input" onChange={this.handleChange} />
<button onClick={this.handlePinSubmit}> Pin Dogrula!</button>
</div>
)
render() {
return (
<div className="App">
<div className="App-intro">
{!this.state.confirm
? this.phoneInput()
: this.pinInput()
}
{this.state.msg}
</div>
</div>
);
}
}
Still, this code can be improved. For example there could be one Input component, we can pass some props to render different input and buttons. But it works according to your needs right now.
You can handle this situation by using a controlled component (docs).
here you need to handle onChange and pass the changed value to input. On button press reset that value
<input
value={this.state.textValue} // whatever value you put in textValue state will be the text appear in input box on UI so
type="text"
onChange={e => this.handleTextChange(e.target.value)}
/>
codesandbox : here
Related
import React from "react";
import axios from "axios";
import { config } from "#fortawesome/fontawesome-svg-core";
import "./AddFolder.css";
export default class AddFolder extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
textarea: "",
};
}
handleCancelBtn() {
window.history.back();
}
updateName(n) {
console.log("updateName, ran: " + n + " type is: " + typeof n);
this.setState({ name: { value: n } });
}
updateTextarea(area) {
console.log("updateTextarea, ran: " + area);
this.setState({ textarea: { value: area } });
}
handleSubmit(e) {
e.preventDefault();
console.log("handleSubmit, ran");
const { name } = this.state;
try {
axios.post(
`${config.API_ENDPOINT}/folders`,
{
name,
}.then((res) => {
console.log(res);
console.log(res.data);
window.location = "/retrieve"; //This line of code will redirect you once the submission is succeed
})
);
console.log("folder created");
} catch (e) {
console.log("there was an error: " + e.message);
}
}
render() {
return (
<form className="addfolder-form" onSubmit={(e) => this.handleSubmit(e)}>
<h2>Add Folder</h2>
<div className="form-group">
<label htmlFor="name">name:</label>
<input
type="text"
className="name-input"
name="name"
id="name"
onChange={(e) => this.updateName(e.target.value)}
/>
</div>
<div className="form-group">
<label htmlFor="text-area">content: </label>
<textarea
id="text-area"
name="text-area"
rows="4"
cols="50"
onChange={(e) => this.updateTextarea(e.target.value)}
/>
</div>
<div className="button-group">
<button
type="reset"
className="addfolder-btn"
onClick={this.handleCancelBtn}
>
nevermind
</button>
<button
type="submit"
className="addfolder-btn"
onClick={this.handleSubmit}
>
save
</button>
</div>
</form>
);
}
}
on this code const { name } = this.state; i get an error of:
TypeError: Cannot read property 'state' of undefined
why is it undefined and how can I post data to my local storage which is stored in ${config.API_ENDPOINT}/folders`, ?
i am trying to add the form inputs into the /folders endpoint using axios, so far the inputs are working, but when i try to submit it, i get the error above. i know i dont have textarea input there yet, but i am testing the name input.
"this" is undefined in the handleSubmit function. Like jayce said you have to bind the funtion in the constructor.
constructor(props) {
super(props);
this.state = {
name: "",
textarea: "",
};
this.handleSubmit = this.handleSubmit.bind(this);
}
My first React session data storage, so thanks. I am trying to set up inputing data and then placing it in session storage, so it can be edited, viewed or deleted later. There are 2 pieces of data, "title" and "note" to be inputed into a form. Nothing happens when I type into the form inputs. Any other help welcome also.
class AddNote extends Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
}
}
componentDidMount() {
this.getFormData();
}
//let notes = getSessionItem(keys.notes);
//if (!notes) { notes = ""; }
onTitleChange(event) {
this.setState({ title: event.target.value }, this.storeFormData);
this.storeFormData();
}
onContentChange(event) {
this.setState({ content: event.target.value }, this.storeFormData);
}
storeFormData() {
const form = {
title: this.state.title,
content: this.state.content
}
setSessionItem(keys.user_form, form);
}
getFormData() {
const form = getSessionItem(keys.user_form);
if (form) {
this.setState({
title: form.name,
content: form.content
});
}
}
render() {
return (
<div>
<div>
<h2>ADD NOTE PAGE</h2>
</div>
<form classname="nav1">
<div>
<label><b>Title</b></label>
<input type="text"
value={this.state.title}
onchange={this.onTitleChange.bind(this)}
/>
</div>
<div>
<label><b>Content</b></label>
<input type="text"
value={this.state.content}
onchange={this.onContentChange.bind(this)}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default AddNote;
and the storage file:
export const keys = {
title: 'title',
notes: 'notes'
}
export const getSessionItem = function (key) {
let item = sessionStorage.getItem(key);
item = JSON.parse(item);
return item;
}
export const setSessionItem = function (key, value) {
value = JSON.stringify(value);
sessionStorage.setItem(key, value);
}
export const removeSessionItem = function (key) {
sessionStorage.removeItem(key);
}
No need to have 2 change handler for your input. You can do it using a common change handler.
<form classname="nav1">
<div>
<label><b>Title</b></label>
<input type="text"
value={this.state.title}
name="title" <---- Provide name here
onChange={this.onChange}
/>
</div>
<div>
<label><b>Content</b></label>
<input type="text"
value={this.state.content}
name="content" <---- Provide name here
onChange={this.onChange}
/>
</div>
<button type="submit">Submit</button>
</form>
Your onChange function should be, and use callback in setState to call your storeFormData function.
onChange = (e) => {
this.setState({
[e.target.name] : e.target.value
}, () => this.storeFormData())
}
Note: In React we use camelCase, for example, onchange should be onChange and classname should be className.
Also make sure you bind this to storeFormData and getFormData functions, or you can use arrow function's to automatically bind this.
Demo
Set the attributes of a input field or any component by taking input from the user dynamically?
I would like to know if there is any way, where I would give user an option to choose a component from the list of components i would mention, and allow him to customize the components attributes. For example if the user chooses a Input component, he must be able to set the attributes of that particular component, like "required", "type", "placeholder".
You can achieve it by passing all attributes you want as props to the child component.
You should also add them to state of parent component with change handler.
Each time the user change something of the attributes, the state should update.
As the state updates, the new state will pass as props to child Component and it'll update.
I made a simple example to input: You can change its placeholder, minLength, and requierd.
Check This Example
in the render, method you can do something like this
render() {
// change the name and values base on your user input
userInputtedAttribName = "placeholder";
userInputtedAttribValue = "the placeholder";
// the object to contain your user defined attribute name and values
const dynamicAttributes = {
[userInputtedAttribName]: userInputtedAttribValue
};
return (
<div>
<input type="text" {...dynamicAttributes}></input>
</div>
)
}
the spread operator, {...dynamicAttributes}, will build the attributes and their values dynamically
Probably not even what you're looking for, but I made a medium-sized prototype that can show you how to create Components (input, button, textarea), dynamically.
It's like filling out a form. Choose a type of component you want to make from the select-list. Then define the attributes you want in the proceeding textboxes. Once you're done adding all the attributes, hit Generate to render your customized component.
Sandbox: https://codesandbox.io/s/dynamic-component-generator-mhuh5
Working code:
import React from "react";
import ReactDOM from "react-dom";
import Input from "./Input";
import Button from "./Button";
import TextArea from "./TextArea";
import "./styles.css";
class ComponentGenerator extends React.Component {
state = {
componentInProgress: null,
customizeMode: false,
attributeName: "",
attributeSetting: "",
boolean: false,
builtComponents: []
};
handleSelection = e => {
this.setState({
componentInProgress: { componentType: e.target.value },
customizeMode: true
});
};
createOptions = () => {
const { customizeMode, componentInProgress } = this.state;
return (
<div>
<h4>Choose a Component:</h4>
<select
onChange={this.handleSelection}
value={!customizeMode ? "Select" : componentInProgress.componentType}
>
<option>Select</option>
<option>Input</option>
<option>TextArea</option>
<option>Button</option>
</select>
</div>
);
};
handleOnChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
handleOnSubmit = e => {
const {
attributeName,
attributeSetting,
boolean,
componentInProgress
} = this.state;
e.preventDefault();
let componentCopy = JSON.parse(JSON.stringify(componentInProgress));
componentCopy.props = {
...componentCopy.props,
[attributeName]: boolean ? boolean : attributeSetting
};
this.setState({
componentInProgress: componentCopy,
attributeName: "",
attributeSetting: "",
boolean: false
});
};
setBoolean = boolean => {
this.setState({
boolean: boolean
});
};
generateComponent = () => {
const { componentInProgress, builtComponents } = this.state;
this.setState({
componentInProgress: null,
customizeMode: false,
builtComponents: [...builtComponents, componentInProgress]
});
};
defineComponentAttributes = () => {
const {
componentInProgress,
attributeName,
attributeSetting,
boolean
} = this.state;
return (
<div>
<h4>
Customizing:{" "}
<span className="highlight">{componentInProgress.componentType}</span>
</h4>
{/*Render form */}
<form onSubmit={this.handleOnSubmit}>
<label>Attribute: </label>
<input
className="form-group"
onChange={this.handleOnChange}
value={attributeName}
name="attributeName"
placeholder="Choose attribute (type)"
/>
<label>Definition: </label>
<input
className="form-group"
onChange={this.handleOnChange}
value={attributeSetting}
name="attributeSetting"
placeholder="Define attribute (text)"
/>
<label>This is a Boolean type: </label>
<input
type="radio"
name="boolean"
onChange={() => this.setBoolean(true)}
/>
True
<input
type="radio"
name="boolean"
checked={boolean === false}
onChange={() => this.setBoolean(false)}
/>
False
<button className="form-group" type="submit">
Add
</button>
</form>
{/*Create List of attributes */}
{componentInProgress.props && (
<div>
<h4>Defined Attributes:</h4>
{Object.entries(componentInProgress.props).map(
([propName, propValue]) => {
return (
<div key={propName}>
<span>{propName}: </span>
<span>{propValue + ""}</span>
</div>
);
}
)}
</div>
)}
<div>
<h4>Click to finish and generate:</h4>
<button onClick={this.generateComponent}>Generate</button>
</div>
</div>
);
};
renderComponents = () => {
const { builtComponents } = this.state;
return builtComponents.map((component, index) => {
let renderedComponent = () => {
switch (component.componentType) {
case "Input":
return <Input {...component.props} />;
case "Button":
return <Button {...component.props} />;
case "TextArea":
return <TextArea {...component.props} />;
default:
return null;
}
};
return (
<div key={index} className="componentSection">
<h4>{component.componentType}</h4>
{renderedComponent()}
<div>
<p>Attributes: </p>
{Object.entries(component.props).map(([propName, propValue]) => {
return (
<div key={propName}>
<span>{propName}: </span>
<span>{propValue + ""}</span>
</div>
);
})}
</div>
</div>
);
});
};
render() {
const { customizeMode } = this.state;
return (
<div>
{this.createOptions()}
{customizeMode && this.defineComponentAttributes()}
<div className="components">{this.renderComponents()}</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ComponentGenerator />, rootElement);
I have two hidden input fields that are being populated with the values from a prop when the node-fetch finishes and the values are being set without any issue, but I see the following warning.
Warning: A component is changing a controlled input of type hidden to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
While I understand the differences between controlled and uncontrolled, I can't seem to understand why my component would be creating a conflict between the two. Is there anything clear in my component code that could be causing this?
import React from 'react';
import isEqual from 'lodash/isEqual';
export default class DateFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
startDateValue: '',
endDateValue: ''
};
}
componentDidMount() {
this.setState({
startDateValue: this.props.startDateQuery,
endDateValue: this.props.endDateQuery
});
}
handleChange(input, value) {
this.setState({
[input]: value
})
}
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
render() {
return (
<div className="col-md-3">
<input type="hidden" name="_csrf" value={this.props.csrf} />
<div className="input-group blog-filter-date-range-picker">
<p>Blog Date Range:</p>
</div>
<div className="input-group blogFilterDatePicker">
<span className="input-group-addon"><i className="glyphicon glyphicon-calendar"></i></span>
<input type="text" name="blogDateRange" className="form-control blogFilterDatePicker" autoComplete="off" />
</div>
<input type="hidden" name="blogStartDate" className="form-control" value={this.state.startDateValue} onChange={e => this.handleChange('startDateValue', e.target.value)} />
<input type="hidden" name="blogEndDate" className="form-control" value={this.state.endDateValue} onChange={e => this.handleChange('endDateValue', e.target.value)} />
</div>
);
}
}
Parent Component:
import React from 'react';
import CatgoryFilter from './SearchFormFilters/CategoryFilter';
import DepartmentFilter from './SearchFormFilters/DepartmentFilter';
import TeamFilter from './SearchFormFilters/TeamFilter';
import TypeFilter from './SearchFormFilters/TypeFilter';
import DateFilter from './SearchFormFilters/DateFilter';
//Activity Feed - Search Form
export default class ActivityFeedSearchForm extends React.Component {
render() {
var clearFilters;
if(this.typeQuery || this.props.categoryQuery || this.props.departmentQuery || this.props.teamQuery || this.props.startDateQuery || this.props.endDateQuery || this.props.typeQuery){
clearFilters = Clear;
}
return (
<div className="row">
<div className="annotation-search-form col-md-10 col-md-offset-1">
<div clas="row">
<form action="/app" method="post" className="annotation-filter-fields">
<DateFilter csrf={this.props.csrf} startDateQuery={this.props.startDateQuery} endDateQuery={this.props.endDateQuery} />
<TypeFilter typeQuery={this.props.typeQuery} />
<CatgoryFilter category={this.props.category} categoryQuery={this.props.categoryQuery} />
<DepartmentFilter department={this.props.department} departmentQuery={this.props.departmentQuery} />
<TeamFilter team={this.props.team} teamQuery={this.props.teamQuery} />
<div className="col-md-1 annotation-filter-section filter-button-container">
<button type="submit" id="annotation-filter-submit">Filter</button>
{clearFilters}
</div>
</form>
</div>
</div>
</div>
)
}
}
Top-level Parent Component:
import React from 'react';
import fetch from 'node-fetch';
import ReactMarkdown from 'react-markdown';
import path from 'path';
import ActivityFeedSearchForm from './ActivityFeedSearchForm/ActivityFeedSearchForm';
import { API_ROOT } from '../config/api-config';
//GET /api/test and set to state
export default class ActivityFeed extends React.Component{
constructor(props, context) {
super(props, context);
this.state = this.context.data || window.__INITIAL_STATE__ || { team: [] };
}
fetchList() {
fetch(`${API_ROOT}` + '/api' + window.location.search, { compress: false })
.then(res => {
return res.json();
})
.then(data => {
this.setState({
startDateQuery: data.startDateQuery,
endDateQuery: data.endDateQuery,
});
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
this.fetchList();
}
render() {
return (
<div>
<Navigation notifications={this.state.notifications}/>
<ActivityFeedSearchForm csrf={this.state.csrf} category={this.state.category} categoryQuery={this.state.categoryQuery} department={this.state.department} departmentQuery={this.state.departmentQuery} team={this.state.team} teamQuery={this.state.teamQuery} typeQuery={this.state.typeQuery} startDateQuery={this.state.startDateQuery} endDateQuery={this.state.endDateQuery} />
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome} />
<LoadingIndicator loading={this.state.isLoading} />
<ActivityFeedLayout {...this.state} />
</div>
</div>
</div>
)
}
};
As you are dealing with only startDateValue and endDateValue in componentWillReceiveProps it’s good to compare them individually instead of whole props. Deep equality check is not happening for whole props and that’s why you get the warning
Change
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
To
componentWillReceiveProps(nextProps) {
if (this.props.startDateQuery != nextProps.startDateQuery && this.props.endDateQuery != nextProps.endDateQuery){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
Also you don’t need componentDidMount so remove that part in your code and update constructor code with below one
constructor(props) {
super(props);
this.state = {
startDateValue: this.props.startDateQuery ? this.props.startDateQuery: '',
endDateValue:this.props.endDateQuery ? this.props.endDateQuery: ''
};
}
}
And
Change
<input type="hidden" name="_csrf" value={this.props.csrf} />
To
<input type="hidden" name="_csrf" value={this.props.csrf ? this.props.csrf : "" />
And you are not binding handleChange so either bind it manually in constructor or change it to arrow function like below
Change
handleChange(input, value) {
this.setState({
[input]: value
})
}
To
handleChange = (input, value) => {
this.setState({
[input]: value
})
}
I have a question about why does not the "onClick" function work? It will only receive "You are not old enough!", when i hit the button. I use a input field.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state= {
term: 'write a number'
}
this.change = this.change.bind(this);
}
change = (event) => {
this.setState({term: event.target.value >= 18 ? <p>You are old enough!
</p> : <p>You are not old enough!</p>});
}
render() {
return (
<div style={{textAlign : "center"}}>
<input type="text"></input><br></br>
<p>Result</p><br></br>
{this.state.term}
<button type="submit" onClick={this.change}>Submit</button>
</div>
);
}
}
export default App;
If you want to validate the input on click, store the value of the input in state.
class App extends Component {
constructor() {
super();
this.state = {
term: 'write a number',
value: ''
};
}
handleChange = event => {
this.setState({
value: event.target.value
});
};
validate = () => {
this.setState({
term:
parseInt(this.state.value) >= 18
? 'You are old enough!'
: 'You are not old enough!'
});
};
render() {
return (
<div style={{ textAlign: 'center' }}>
<input
type="text"
onChange={this.handleChange}
value={this.state.value}
/>
<br />
<p>Result</p>
<br />
<p>{this.state.term}</p>
<button type="submit" onClick={this.validate}>
Submit
</button>
</div>
);
}
}
You can create a handler for the input and when you click in the button you get the value from the state.
Check it out my approach.
class App extends React.Component {
state = {
age: null,
term: 'write a number'
}
onClick = () => {
if(this.state.age) {
const output = this.state.age >= 18 ?
<p>You are old enough!</p> :
<p>You are not old enough!</p>
this.setState({
term: output
});
}
onInputHandler = (event) => {
this.setState({age: event.target.value})
}
render() {
return (
<div style={{textAlign : "center"}}>
<input type="text" onChange={e => this.onInputHandler(e)}></input><br></br>
<p>Result</p><br></br>
<button onClick={this.onClick}>Submit</button>
</div>);
}
}