import React from 'react';
import {render} from 'react-dom';
class Form extends React.Component {
constructor(props) {
super(props);
this.validate = this.validate.bind(this);
}
componentDidMount() {
}
validate() {
this.props.children.map((field)=> {
field.validate();
});
}
render() {
return (
<form className={this.props.className}>
{this.props.children}
</form>
);
}
}
export default Form;
Above is the Form.jsx
import React from 'react';
import '../form.css';
class TextField extends React.Component {
constructor(props) {
super(props);
this.state = {
valid: true
};
this.validate = this.validate.bind(this);
}
validate() {
if (!!this.props.required) {
if (this.refs.field.value.trim().length === 0) {
this.setState({
valid: false
});
return false;
} else {
this.setState({
valid: true
});
return true;
}
}
return true;
}
setValue(event) {
if (this.validate()) {
this.props.setValue(this.props.name, event.target.value);
}
}
render() {
var input = (
<span className={this.state.valid ? null : 'field-invalid'}>
<input ref="field" type="text" name={this.props.name} placeholder={this.props.placeholder}
onBlur={this.setValue.bind(this)}/>
</span>
);
var field = input;
if (this.props.label) {
field = (
<div className="row">
<label className={"col-3 align-r" + (!!this.props.required ? " field-required" : "")}>
{this.props.label}
</label>
<span className="col-6">
{input}
</span>
</div>
);
}
return field;
}
}
export default TextField;
this is a field and contains validate method. but this method is not accessible from Form.jsx this.props.children.
Another parent Contains
<Form ref={(form)=> {this.form = form;}} className="label-input-group">
<TextField label="Vehicle Owner" required={true} name="owner"/>
</Form>
validate function is undefined and throwing error. this.props.children not updated in the parent after children mount i think. Any way to make it work?
I'm not sure it's a good idea, but you can do something like that :
// Form
constructor(props) {
super(props);
this.validate = this.validate.bind(this);
this.childrenInstances = [];
this.props.children.forEach(field => {
field.ref = inst => this.childrenInstances.push(inst);
});
}
validate() {
this.childrenInstances.forEach(field => {
field.validate();
});
}
Related
I'm currently working on a project that uses QuillJS for a rich text editor. I need to post the rich text content to my backend but I'm not sure how to access the QuillJS output.
In RichTextEditor.js
import React, { Component } from "react";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
class RichTextEditor extends Component {
constructor(props) {
super(props);
// this.formats = formats;
this.state = { text: "" }; // You can also pass a Quill Delta here
this.handleChange = this.handleChange.bind(this);
}
handleChange(value) {
this.setState({ text: value });
const text = this.state;
console.log(text);
}
render() {
return (
<ReactQuill
value={this.state.text}
onChange={this.handleChange}
formats={this.formats}
modules={this.modules}
/>
);
}
}
export default RichTextEditor;
The console.log(text) basically just outputs the content of the rich text editor. Something like this "<p><em>aasdasdasd</em><strong><em>asdasdasdasd</em></strong></p>"
In Post.js
import React, { Component } from "react";
import RichTextEditor from "./RichTextEditor.js";
import "../../css/Post.css";
class Post extends Component {
constructor(props) {
super(props);
this.state = {
question: "",
};
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
};
handleSubmit = (e) => {
e.preventDefault();
const { question } = this.state;
console.log("Question");
console.log(question);
render() {
const { question } = this.state;
return (
<div className="post">
<div className="post__container">
<form onSubmit={this.handleSubmit}>
<div className="post__richTextEditor">
<RichTextEditor value={question} onChange={this.onChange} name="question" />
</div>
</form>
</div>
</div>
);
}
}
export default Post;
I'm trying to update the state of the question but it doesn't seem to be updating. console.log(question) only outputs a single string.
How can I access the same string output from RichTextEditor.js?
Your RichTextEditor component should not handle change, it should only receive props from higher component:
class RichTextEditor extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ReactQuill
value={this.props.value}
onChange={this.props.onChange}
formats={this.formats}
modules={this.modules}
/>
);
}
}
export default RichTextEditor;
Then your Post component pass value and onChange props to RichTextEditor:
class Post extends Component {
constructor(props) {
super(props);
this.state = {
question: "",
};
this.onChange = this.onChange.bind(this);
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
};
handleSubmit = (e) => {
e.preventDefault();
const { question } = this.state;
console.log("Question");
console.log(question);
render() {
const { question } = this.state;
return (
<div className="post">
<div className="post__container">
<form onSubmit={this.handleSubmit}>
<div className="post__richTextEditor">
<RichTextEditor value={question} onChange={this.onChange} name="question" />
</div>
</form>
</div>
</div>
);
}
}
in RichTextEditor.js
handleChange(value) {
this.setState({ text: value });
const text = this.state;
console.log(text);
props.onChange(text); // passing the inner State to parent as argument to onChange handler
}
Now in Post.js
onChange = (newStateString) => {
//this.setState({ [e.target.name]: e.target.value });
console.log(newStateString); // you should get the string here
};
In the same code, I was able to the get the grandparent component's setState method to update accordingly for an onClick event from the grandchild component, however, for the onChange event, it is failing. I am not getting any errors.
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber=()=> {
this.props.changeNumber();//call child method
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return(
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value = {data} onChange={this.handleChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return(
<div>
<GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.handleChange}/>
</div>
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
number: 1,
data: ""
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(data) {
this.setState({data:this.state.data});
console.log(data);
}
changeNumber=()=>{
this.setState((prevState)=>{
console.log(prevState,this.state.data);
return {
number : prevState.number + 1
}
});
}
render() {
const data = this.state.data;
const input = data;
return (
<Child number={this.state.number}
changeNumber = {this.changeNumber}
data={input}
onChange = {this.handleChange}
/>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Console Result:
Object {
data: "",
number: 1
} ""
result screenshot:
console.log result
see code pen for live code:
https://codepen.io/codehorse/pen/yLyEwBw?editors=0011
Your improved code with live demo https://codesandbox.io/s/laughing-sky-kk97b
What need to change <GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.onChange}/>
Complete Code
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber = () => {
this.props.changeNumber(); //call child method
};
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return (
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value={data} onChange={this.props.onChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild
number={this.props.number}
changeNumber={this.props.changeNumber}
value={this.props.data}
onChange={this.props.onChange}
/>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 1,
data: ""
};
}
handleChange = e => {
this.setState({ data: e.target.value });
console.log(e.target.value);
};
changeNumber = () => {
this.setState(prevState => {
console.log(prevState, this.state.data);
return {
number: prevState.number + 1
};
});
};
render() {
const data = this.state.data;
const input = data;
return (
<Child
number={this.state.number}
changeNumber={this.changeNumber}
data={input}
onChange={this.handleChange}
/>
);
}
}
export default App;
I am trying to get the user to input data for my TODO App instead of picking up data from a predefined array.
Below is my Parent component
import React from "react"
import TodoItem from "./TodoItem"
import todosData from "./todosData"
class App extends React.Component {
constructor() {
super()
this.state = {
name: "",
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
this.nameEnter = this.nameEnter.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(item => {
if (item.id === id){
return {
...item,
completed: !item.completed
}
}
return item
})
return {
todos: updatedTodos
}
})
}
nameEnter(){
var name = this.name
console.log(name)
}
render() {
const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item}
handleChange = {this.handleChange} nameEnter= {this.nameEnter}/>)
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App
This one is the child component where I have added input fields
import React from "react"
function TodoItem(props) {
return (
<div className="todo-item">
<input
type = "checkbox"
checked = {props.item.completed}
onChange = { () => props.handleChange(props.item.id) }
/>
<input type = "text" name= "name" />
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
This is how my page looks. Instead of predefined text example:"GROCERRY SHOPPING", I want the user to enter text and it should be in place of that.
Ok so here it is
<input onChange={(e)=>handleChange(e)}/>
//react hooks
const [values,setValues] = useState({inputValue: 'predefined'});
handleChange = (e)=>{
setValues({...values,[e.target.name]: e.target.value})
}
//classes
handleChange = (e)=>{
this.setState({[e.target.name]:e.target.value});
}
this generic and can apply to a number of inputs, not just one
import React from "react"
class Form extends React.Component {
constructor() {
super()
this.state = {
name: ""
}
}
onChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
const {name} = this.state
return (
<input type="text" name="name" value={name} onChange={this.onChange}/>
)
}
}
export default Form
changeInputVal (ev, index) {
ev.persist();
const newArr = this.state.inputArrVal;
newArr[index]=ev.target.value;
this.setState({inputArrVal:newArr, currentPage:1});
}
you need to do something like that in TodoItem --
where --
1- inputArrVal:{} is object And
2- "this.changeInputVal(event, props.item.id)}/>"
I am new to react. I am trying to create a simple todolist using store2.js library. There is a problem in rendering the elements in that to do list.
var React = require('react');
var store = require("store2");
import {Button} from "react-bootstrap";
class CreateToDoList extends React.Component {
componentDidMount() {
this.inputVal="";
}
constructor() {
super();
this.addValueToList = this.addValueToList.bind(this);
this.handleInput=this.handleInput.bind(this);
};
handleInput(e)
{
this.inputValue=e.target.value;
}
addValueToList(){
if(store.has("todoList")===false)
{
store.set("todoList",{count:0})
}
var count=store.get("todoList").count;
count+=1;
var obj={
value:this.inputValue,
isChecked:false
};
store.transact("todoList",function(elements){
elements[count+""]=obj;
elements.count=count
});console.log(store.getAll("todoList"));debugger;
}
render() {
return ( <div>
<input type="input" onChange={this.handleInput}/>
<Button bsStyle="primary" onClick={this.addValueToList}>Add</Button>
<CreateShowPreviousTasks/>
</div>
)
}
}
class todoValues extends React.Component{
componentDidMount(){
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
}
render(){
console.log(this.props)
return(
<div >
<input type="checkbox" checked={this.props.isCheck}></input>
<input type="input">{this.prop.value}</input>
</div>
)
}
}
class CreateShowPreviousTasks extends React.Component {
componentDidMount() {
console.log("here")
}
constructor(){
super();
this.handleClick=this.handleClick.bind(this);
}
handleClick(event)
{
}
render() {
if (store.has('todoList') !== undefined) {
var divElements=[];
this.loop=0;
var count=store.get("todoList").count;
for(this.loop=0;this.loop<count;this.loop++)
{
var obj=store.get("todoList");
obj=obj[count+""];
divElements.push(
<todoValues value={obj.value} key={this.loop+1}/>
)
}
} else {
store.set('todoList',{
count:0
})
}
return (<div>{divElements}</div>
)
}
}
export default CreateToDoList;
The class todoValues adds the div elements of two input buttons wrapped in a div. But the rendering is only done as <todovalues value="as"></todovalues>.
The class CreateShowPreviousTasks which retrieves the list of stored items in the local storage items and passing those values as properties to todoValues and wrapping as in a div.
use the folliwng syntax to render a list
render() {
let todos = store.get("todolist");
return (
<div>
{
Object.keys(todos).map(function(key){
let todo = todos[key];
return (<todoValues value={ todo.value } key={ key }/>)
})
}
</div>
}
Does this answer your question?
i'm pretty new to react and redux and i'm having an issue here. It's mandatory to use only stateless components with containers whenever state handing is required. These two components are:
import React from 'react';
import DatePicker from '../DatePicker';
class DayInput extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
dateValue: new Date(),
activeDateWidget: false,
};
}
changeDate(date) {
this.setState({
dateValue: date,
});
}
changeActiveDateWidget(e) {
e.stopPropagation();
this.setState({
activeDateWidget: !this.state.activeDateWidget,
});
}
render() {
const { input, meta } = this.props;
const { dateValue, activeDateWidget } = this.state;
return (
<div>
<input
{...input}
className="form-control"
type="text"
value={dateValue}
onClick={this.changeActiveDateWidget}
// onBlur={this.changeActiveDateWidget}
/>
{activeDateWidget ? (
<div>
<DatePicker
changeActiveDateWidget={this.changeActiveDateWidget}
changeDate={this.changeDate}
dateValue={dateValue}
/>
</div>
) : (
<div />
)}
</div>
);
}
}
export default DayInput;
import React from 'react';
import 'react-day-picker/lib/style.css';
import DayPicker, { DateUtils } from 'react-day-picker';
class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
selectedDay: new Date(),
};
}
componentDidMount() {
if (this.input) {
this.input.focus();
}
}
handleDayClick(e, day, { disabled }) {
e.stopPropagation();
if (disabled) {
return;
}
this.setState({ selectedDay: day }, () => {
this.props.changeDate(day);
this.props.changeActiveDateWidget();
});
}
focusThisComponent(e) {
if (e) {
this.input = e;
}
}
render() {
const { changeActiveDateWidget } = this.props;
const { selectedDay } = this.state;
return (
<div
ref={this.focusThisComponent}
tabIndex="1"
>
<DayPicker
id="THISTHING"
initialMonth={selectedDay}
selectedDays={day => DateUtils.isSameDay(selectedDay, day)}
onDayClick={this.handleDayClick}
/>
</div>
);
}
}
export default DatePicker;
As you can see the first component is wrapped inside the second component. I tried to convert the first component myself like this:
const DayInput = props => {
<input
{...props.input}
type="text"
value= {new Date()}
onClick={()=>??}
/>
}
but as you can see i dont know how to handle the onclick event. Can someone help me to achieve this?
To turn your component in a stateless component, you must pass everything as properties of the component.
This will be your DayInput splitted into 2 components :
const DayInputShow = props => {
return (<input
{...props.input}
type="text"
value= {props.value}
onClick={(event)=>props.onClick()}
/>);
};
const DayInputEdit = props => {
return (<DatePicker
changeActiveDateWidget={props.changeActiveDateWidget}
changeDate={props.onChange}
dateValue={props.value}
/>);
};
DayInputShow.propTypes = {
value: PropTypes.date,
onClick: PropTypes.func,
}
DayInputEdit.propTypes = {
value: PropTypes.date,
onChange: PropTypes.func,
}
And this will be the root component (uncomplete and still statefull) :
class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
selectedDay: new Date(),
};
}
componentDidMount() {
if (this.input) {
this.input.focus();
}
}
handleDayClick(e, day, { disabled }) {
e.stopPropagation();
if (disabled) {
return;
}
this.setState({ selectedDay: day }, () => {
this.props.changeDate(day);
this.props.changeActiveDateWidget();
});
}
focusThisComponent(e) {
if (e) {
this.input = e;
}
}
render() {
const { changeActiveDateWidget } = this.props;
const { selectedDay } = this.state;
let dayPicker;
if (this.input) {
dayPicker = <DayPickerEdit
value={this.state.selectedDay}
onChange={(value) => {this.setState({selectedDay: value})}}
selectedDays={day => DateUtils.isSameDay(selectedDay, day)}
onDayClick={this.handleDayClick}
/>
} else {
dayPicker = <DayPickerShow
value={this.state.selectedDay}
ref={(input) => { this.inputRef = input; }} />
onClick={() => {this.focusThisComponent(this.inputRef )}}
/>
}
return (
<div
ref={this.focusThisComponent}
tabIndex="1"
>
{dayPicker }
</div>
);
}
}
export default DatePicker;