import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
data: 'First Comes First Serves'
}
this.updateState = this.updateState.bind( this );
};
updateState( e ) {
console.log( e );
// console.log( e.target );
this.setState( {data: e} );
}
render() {
return (
<div>
<Content myDataProp = { this.state.data } updateStateProp = { this.updateState }></Content>
</div>
);
}
}
class Content extends React.Component {
render() {
return (
<div>
<input type="text" value={ this.props.myDataProp } id="id" />
<div>
<button onClick={ this.props.updateStateProp( document.getElementById( 'id' ).value )}>Click</button>
</div>
<h3>{ this.props.myDataProp }</h3>
</div>
);
}
}
export default App;
Error;
Uncaught TypeError: Cannot read property 'value' of null
I'm very new at React.
Here, input value is assigned as 'First come..'(I suppose so), but error appears,
Thanks in advance.
When you are working with React, it is usually best to completely forget about operations that work directly on the DOM like getElementById. They will either not work at all or in unexpected ways.
Instead, have a look at the excellent React forms documentation.
The idiomatic way to do forms in React is to implement the onChange event handler of the input and store the current value in the compoenent's state. Then you can use it access and use it when the user presses the button.
I totally agree with Timo. I have modified the component for you:
class App extends React.Component {
constructor() {
super();
this.state = {
data: 'First Comes First Serves'
}
this.updateState = this.updateState.bind(this);
};
updateState(e) {
this.setState({ data: e.target.value });
}
render() {
return (
<div>
<Content myDataProp={this.state.data} updateStateProp={this.updateState}></Content>
</div>
);
}
}
class Content extends React.Component {
render() {
return (
<div>
<input type="text" onChange={this.props.updateStateProp} id="id" />
<div>
<button onClick={this.props.updateStateProp}>Click</button>
</div>
<h3>{this.props.myDataProp}</h3>
</div>
);
}
}
export default App;
Let me know if you are able to get it working.
Related
How to update state of one component in another in react class component.
I have two class in reacts.
MyComponent and MyContainer.
export default class MyContainer extends BaseComponent{
constructor(props: any) {
super(props, {
status : false,
nameValue :"",
contentValue : ""
});
}
componentDidMount = () => {
console.log(this.state.status);
};
save = () => {
console.log("Hello I am Save");
let obj: object = {
nameValue: this.state.nameValue, // here I am getting empty string
templateValue: this.state.contentValue
};
// API Call
};
render() {
return (
<div>
<MyComponent
nameValue = {this.state.nameValue}
contentValue = {this.state.contentValue}
></MyComponent>
<div >
<button type="button" onClick={this.save} >Save</button>
</div>
</div>
);
}
}
MyComponent
export default class MyComponent extends BaseComponent{
constructor(props: any) {
super(props, {});
this.state = {
nameValue : props.nameValue ? props.nameValue : "",
contentValue : props.contentValue ? props.contentValue : "",
status : false
}
}
componentDidMount = () => {
console.log("MOUNTING");
};
fieldChange = (id:String, value : String) =>{
if(id === "content"){
this.setState({nameValue:value});
}else{
this.setState({contentValue:value});
}
}
render() {
return (
<div>
<div className="form-group">
<input id="name" onChange={(e) => {this.fieldChange(e)}}></input>
<input id = "content" onChange={(e) => {this.fieldChange(e)}} ></input>
</div>
</div>
);
}
}
In MyComponent I have placed two input field where on change I am changing the state.
Save button I have in MyContainer. In save button I am not able to read the value of MyComponent. What is the best way to achieve that.
You should be updating your state in MyContainer for save to have visibility of the state changes. Each component gets its own state, which makes MyComponent's state unique to that of MyContainer. What you should be doing is keeping the state in your parent/container component, and then passing it down as props (rather than duplicating it in your child). To do this, move fieldChange up to the MyContainer function, and remove the duplicate nameValue and contentValue state within MyComponent. See code commennts for further details:
export default class MyContainer extends BaseComponent{
...
fieldChange = (id:String, value : String) =>{
if(id === "content"){
this.setState({nameValue: value});
} else {
this.setState({contentValue: value});
}
}
render() {
return (
<div>
<MyComponent
nameValue={this.state.nameValue}
contentValue={this.state.contentValue}
onFieldChange={this.fieldChange} /* <---- Pass the function down to `MyComponent` */
/>
...
</div>
);
}
}
Then in MyComponent, call this.props.onFieldChange:
export default class MyComponent extends BaseComponent{
// !! this constructor can be removed as no state is being initialized anymore !!
constructor(props: any) {
super(props);
// removed state as we're using the state from `MyContainer`
}
componentDidMount = () => {
console.log("MOUNTING");
};
render() {
return (
<div>
<div className="form-group">
<input id="name" onChange={(e) => {this.props.fieldChange(e)}} /> /* <--- Change to `this.props.fieldChange()`. `<input />` is a self-closing tag.
<input id = "content" onChange={(e) => {this.props.fieldChange(e)}} />
</div>
</div>
);
}
}
Some additional notes:
If your component doesn't use this.props.children, then you should call it as <MyComponent ... props ... /> not <MyComponent ... props ...></MyComponent>
Your if-statement in your fieldChange looks reversed and should be checking if(id === "name"). I'm assuming this is an error in your question.
You're only passing one argument to fieldChange in your example code. I'm again assuming this in an error in your question.
I'm currently following this and I did get it to work. But I would like to know if there is a way to stop the Query Render from reloading the data when calling this.setState(). Basically what I want is when I type into the textbox, I don't want to reload the data just yet but due to rendering issues, I need to set the state. I want the data to be reloaded ONLY when a button is clicked but the data will be based on the textbox value.
What I tried is separating the textbox value state from the actual variable passed to graphql, but it seems that regardless of variable change the Query will reload.
Here is the code FYR.
const query = graphql`
query TestComponentQuery($accountId: Int) {
viewer {
userWithAccount(accountId: $accountId) {
name
}
}
}
`;
class TestComponent extends React.Component{
constructor(props){
super(props);
this.state = {
accountId:14,
textboxValue: 14
}
}
onChange (event){
this.setState({textboxValue:event.target.value})
}
render () {
return (
<div>
<input type="text" onChange={this.onChange.bind(this)}/>
<QueryRenderer
environment={environment}
query={query}
variables={{
accountId: this.state.accountId,
}}
render={({ error, props }) => {
if (error) {
return (
<center>Error</center>
);
} else if (props) {
const { userWithAccount } = props.viewer;
console.log(userWithAccount)
return (
<ul>
{
userWithAccount.map(({name}) => (<li>{name}</li>))
}
</ul>
);
}
return (
<div>Loading</div>
);
}}
/>
</div>
);
}
}
Okay so my last answer didn't work as intended, so I thought I would create an entirely new example to demonstrate what I am talking about. Simply, the goal here is to have a child component within a parent component that only re-renders when it receives NEW props. Note, I have made use of the component lifecycle method shouldComponentUpdate() to prevent the Child component from re-rendering unless there is a change to the prop. Hope this helps with your problem.
class Child extends React.Component {
shouldComponentUpdate(nextProps) {
if (nextProps.id === this.props.id) {
return false
} else {
return true
}
}
componentDidUpdate() {
console.log("Child component updated")
}
render() {
return (
<div>
{`Current child ID prop: ${this.props.id}`}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
id: 14,
text: 15
}
}
onChange = (event) => {
this.setState({ text: event.target.value })
}
onClick = () => {
this.setState({ id: this.state.text })
}
render() {
return (
<div>
<input type='text' onChange={this.onChange} />
<button onClick={this.onClick}>Change ID</button>
<Child id={this.state.id} />
</div>
)
}
}
function App() {
return (
<div className="App">
<Parent />
</div>
);
}
I am sorry for asking this, since I think this has been asked before. However I do not understand react enough, or at all to understand the answers people have given on other questions. Neither to implement them into the code I have.
this is the main code:
import React from 'react';
import ReactDOM from 'react-dom';
import TodoItem from './components/TodoItem';
class App extends React.Component {
componentWillMount() {
this.setState({todoList: [], inputField: ''});
}
handleInput(event) {
this.setState({inputField: event.target.value});
}
addTodo(event) {
if(this.state.inputField.length === 0 || event.keyCode && event.keyCode !== 13) return;
event.preventDefault();
var newTodo = {
text: this.state.inputField,
created_at: new Date(),
done: false
};
var todos = this.state.todoList;
todos.push(newTodo);
this.setState({todoList: todos, inputField: ''});
}
render() {
return (
<div>
<ul>
{
this.state.todoList.map(function(todo, index){
return (
<TodoItem todo={todo} key={index} />
);
})
}
</ul>
<div>
<label htmlFor="newTodo">Add Todo item</label>
<input name="newTodo" value={this.state.inputField} type="text" onKeyUp={this.addTodo.bind(this)} onChange={this.handleInput.bind(this)} />
<button type="button" onClick={this.addTodo.bind(this)} >Add</button>
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
and this is the other part:
import React from 'react';
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.state = {todo: props.todo};
}
toggleDone(event) {
var currentTodo = this.state.todo;
if (currentTodo.done) {
currentTodo.done = false;
} else {
currentTodo.done = true;
}
this.setState({todo: currentTodo});
}
removeTodo(event) {
event.preventDefault();
var todos = this.state.todoList;
todos.remove(this);
}
render() {
return (
<li>
<input type="checkbox" onChange={this.toggleDone.bind(this)} />
<span className={this.state.todo.done ? 'done' : ''} >
{this.state.todo.text}</span>
<button type="button" onClick={this.removeTodo.bind(this)}
>X</button>
</li>
);
}
}
export default TodoItem;
Firstly I had the remove function in the main code, but I got an uncaught type error than because it couldn't find the bind??
And when I put it in the second part of code, I get a cannot read property "remove" of undefined error.
Any help would be awesome!
Thx upfront
Remove removeTodo function from TodoItem component and put it in App component. Pass this function as prop to TodoItem component and call this function at cross button click. Remember, bind removeTodo function to App component after moving it.
Not able to get values of input type using this.refs...
how to get that values from input type
export class BusinessDetailsForm extends Component {
submitForm(data) {
console.log(this.refs.googleInput.value)
}
}
reder() {
return(
<form onSubmit={this.submitForm}>
<Field type="text"
name="location"
component={GoogleAutoComplete}
id="addressSearchBoxField"
ref="googleInput"
/>
</form>
)
}
}
You should avoid ref="googleInput" as it is now considered legacy. You should instead declare
ref={(googleInput) => { this.googleInput = googleInput }}
Inside of your handler, you can use this.googleInput to reference the element.
Then inside of your submitForm function, you can obtain the text value with
this.googleInput._getText()
String refs are legacy
https://facebook.github.io/react/docs/refs-and-the-dom.html
If you worked with React before, you might be familiar with an older API where the ref attribute is a string, like "textInput", and the DOM node is accessed as this.refs.textInput. We advise against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases. If you're currently using this.refs.textInput to access refs, we recommend the callback pattern instead.
Edit
From React 16.3, the format for creating refs are:
class Component extends React.Component
{
constructor()
{
this.googleInput = React.createRef();
}
render()
{
return
(
<div ref={this.googleInput}>
{/* Details */}
</div>
);
}
}
using ref={ inputRef => this.input = inputRef } is considered legacy now. In React 16.3 onwards, you can use the code below,
class MyForm extends React.Component {
constructor(props) {
//...
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
EDIT: thanks for the comment #stormwild
In case any one is wondering how to implement ref with hooks :
// Import
import React, { useRef } from 'react';
const Component = () => {
// Create Refs
const exampleInput = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const inputTest = exampleInput.current.value;
}
return(
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" ref={exampleInput} />
</label>
<input type="submit" value="Submit" />
</form>
}
getValue: function() {
return this.refs.googleInput.value;
}
I think the more idiomatic way is to use state instead of refs, although it's a little more code in this case since you only have a single input.
export class BusinessDetailsForm extends Component {
constructor(props) {
super(props);
this.state = { googleInput: '' };
this.defaultValue = 'someValue';
this.handleChange = this.handleChange.bind(this);
this.submitForm = this.submitForm.bind(this);
}
handleChange(e) {
const { field, value } = e.target;
this.setState({ [field]: value });
}
submitForm() {
console.log(this.state.googleInput);
}
render() {
return (
<Formsy.Form onSubmit={this.submitForm} id="form_validation">
<Field type="text"
name="googleInput"
onChange={this.handleChange}
component={GoogleAutoComplete}
floatingLabelText="location"
hintText="location"
id="addressSearchBoxField"
defaultValue={this.defaultValue}
onSelectPlace={this.handlePlaceChanged}
validate={[ required ]}
/>
</Formsy.Form>
);
}
}
See https://facebook.github.io/react/docs/forms.html#controlled-components.
Using RN 0.57.8 when tried this.googleInput._getText(), It resulted in error _getText is not a function so i printed this.googleInput in console and found that _getText() is a function inside _root
this.googleInput._root._getText()
this.googleInput._root._lastNativeText - This will return the last state not the current state please be careful while using it.
In 2018 you should write in constructor this:
In constructor of class you should add something like
this.input = React.createRef()
Examples here:
https://reactjs.org/docs/uncontrolled-components.html
I tried the answer above (https://stackoverflow.com/a/52269988/1978448) and found it only worked for me when I put the refs in the state, but not when I just made them properties of the component.
Constructor:
this.state.refs={
fieldName1: React.createRef(),
fieldName2: React.createRef()
};
and in my handleSubmit I create a payload object to post to my server like this:
var payload = {
fieldName1: this.state.refs.fieldName1.current.value,
fieldName2: this.state.refs.fieldName2.current.value,
}
The react docu explains it very well: https://reactjs.org/docs/refs-and-the-dom.html
this is considered legacy:
yourHandleMethod() {
this.googleInput.click();
};
yourRenderCode(){
ref={(googleInput) => { this.googleInput = googleInput }}
};
whereas, this is considered the way to go:
constructor(props){
this.googleInput = React.createRef();
};
yourHandleMethod() {
this.googleInput.current.click();
};
yourRenderCode(){
<yourHTMLElement
ref={this.googleInput}
/>
};
From React 16.2, you can use: React.createRef
See more: https://reactjs.org/docs/refs-and-the-dom.html
1. using ref={ inputRef => this.input = inputRef }
Exam:
import React, { Component } from 'react';
class Search extends Component {
constructor(props) {
super(props);
this.name = React.createRef();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onSearch(`name=${this.name.value}`);
}
render() {
return (
<div>
<input
className="form-control name"
ref={ n => this.name = n }
type="text"
/>
<button className="btn btn-warning" onClick={ this.handleClick }>Search</button>
</div>
);
}
}
export default Search;
ref={ n => this.name = n } Use Callback Refs -> see
Or:
2. this.name.current.focusTextInput()
class Search extends Component {
constructor(props) {
super(props);
this.name = React.createRef();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onSearch(`name=${this.name.current.value}`);
}
render() {
return (
<div>
<input
className="form-control name"
ref={this.name}
type="text"
/>
<button className="btn btn-warning" onClick={ this.handleClick }>Search</button>
</div>
);
}
}
export default Search;
Hope it will help you.
I'm teaching myself react with a super simple app that asks the user to type a word presented in the UI. If user enters it correctly, the app shows another word, and so on.
I've got it almost working, except for one thing: after a word is entered correctly, I need to clear the input element. I've seen several answers here about how an input element can clear itself, but I need to clear it from the component that contains it, because that's where the input is checked...
// the app
class AppComponent extends React.Component {
constructor() {
super();
this.state = {
words: ['alpha', 'bravo', 'charlie'],
index: 0
};
}
renderWordsource() {
const word = this.state.words[this.state.index];
return <WordsourceComponent value={ word } />;
}
renderWordinput() {
return <WordinputComponent id={1} onChange={ this.onChange.bind(this) }/>;
}
onChange(id, value) {
const word = this.state.words[this.state.index];
if (word == value) {
alert('yes');
var nextIndex = (this.state.index == this.state.words.count-1)? 0 : this.state.index+1;
this.setState({ words:this.state.words, index:nextIndex });
}
}
render() {
return (
<div className="index">
<div>{this.renderWordsource()}</div>
<div>{this.renderWordinput()}</div>
</div>
);
}
}
// the input component
class WordinputComponent extends React.Component {
constructor(props) {
this.state = { text:''}
}
handleChange(event) {
var text = event.target.value;
this.props.onChange(this.props.id, text);
}
render() {
return (
<div className="wordinput-component">
<input type="text" onChange={this.handleChange.bind(this)} />
</div>
);
}
}
See where it says alert('yes')? That's where I think I should clear the value, but that doesn't make any sense because it's a parameter, not really the state of the component. Should I have the component pass itself to the change function? Maybe then I could alter it's state, but that sounds like a bad idea design-wise.
The 2 common ways of doing this is controlling the value through state in the parent or using a ref to clear the value. Added examples of both
The first one is using a ref and putting a function in the child component to clear
The second one is using state of the parent component and a controlled input field to clear it
class ParentComponent1 extends React.Component {
state = {
input2Value: ''
}
clearInput1() {
this.input1.clear();
}
clearInput2() {
this.setState({
input2Value: ''
});
}
handleInput2Change(evt) {
this.setState({
input2Value: evt.target.value
});
}
render() {
return (
<div>
<ChildComponent1 ref={input1 => this.input1 = input1}/>
<button onClick={this.clearInput1.bind(this)}>Clear</button>
<ChildComponent2 value={this.state.input2Value} onChange={this.handleInput2Change.bind(this)}/>
<button onClick={this.clearInput2.bind(this)}>Clear</button>
</div>
);
}
}
class ChildComponent1 extends React.Component {
clear() {
this.input.value = '';
}
render() {
return (
<input ref={input => this.input = input} />
);
}
}
class ChildComponent2 extends React.Component {
render() {
return (
<input value={this.props.value} onChange={this.props.onChange} />
);
}
}
ReactDOM.render(<ParentComponent1 />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
I had a similar issue: I wanted to clear a form which contained multiple fields.
While the two solutions by #noveyak are working fine, I want to share a different idea, which gives me the ability to partition the responsibility between parent and child: parent knows when to clear the form, and the items know how to react to that, without using refs.
The idea is to use a revision counter which gets incremented each time Clear is pressed and to react to changes of this counter in children.
In the example below there are three quite simple children reacting to the Clear button.
class ParentComponent extends React.Component {
state = {revision: 0}
clearInput = () => {
this.setState((prev) => ({revision: prev.revision+1}))
}
render() {
return (
<div>
<ChildComponent revision={this.state.revision}/>
<ChildComponent revision={this.state.revision}/>
<ChildComponent revision={this.state.revision}/>
<button onClick={this.clearInput.bind(this)}>Clear</button>
</div>
);
}
}
class ChildComponent extends React.Component {
state = {value: ''}
componentWillReceiveProps(nextProps){
if(this.props.revision != nextProps.revision){
this.setState({value : ''});
}
}
saveValue = (event) => {
this.setState({value: event.target.value})
}
render() {
return (
<input value={this.state.value} onChange={this.saveValue} />
);
}
}
ReactDOM.render(<ParentComponent />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
EDIT:
I've just stumbled upon this beautifully simple solution with key which is somewhat similar in spirit (you can pass parents's revision as child's key)
Very very very simple solution to clear form is add unique key in div under which you want to render form from your child component key={new Date().getTime()}:
render(){
return(
<div className="form_first_step fields_black" key={new Date().getTime()}>
<Form
className="first_step">
// form fields coming from child component
<AddressInfo />
</div>
</Form>
</div>
)
}