React ref current is NULL - reactjs

I have this code taken from the official React Ref docs
import React from "react";
import { render } from "react-dom";
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
// this.textInput.current.focus();
console.log(this.textInput);
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
const A = () => {
return <div>
<CustomTextInput />
</div>
}
render(<div><A/></div>, document.getElementById("root"));
and when the focusTextInput is called it logs "current: null".
Here is the demo: https://codesandbox.io/s/wo6qjk9xk7

The code is fine since it's the exact same example present in react docs. Problem is your react-dom version is older. React.createRef() API was introduced in React 16.3 (all react related packages should be 16.3+ in order to use React.createRef()). Check this reference in docs.
Your dependencies:
"dependencies": {
"react": "16.6.3",
"react-dom": "16.2.0"
},
Problem fixed after updating react-dom to 16.6.3
Check this: https://codesandbox.io/s/n7ozx9kr0p

Related

How to check whether React updates dom or not?

I wanted to check how to react does reconciliation so I updated the inner HTML of id with the same text. Ideally, it shouldn't update the dom but it is paint reflashing in chrome.
I have tried paint reflashing in chrome it is showing green rectangle over that same text
import React from 'react';
function App() {
return (
<div >
<p id="abc" key="help">abc is here</p>
<button onClick={function () {
// document.getElementById("may").innerHTML = "";
document.getElementById("abc").innerHTML = "abc is here";
}} > Btn</button>
</div>
);
}
export default App;
Expected result should be that paint reflashing shouldn't happen but it is happening.
You are not using React here to update the text of your p tag but directly updating the DOM with JavaScript.
So React reconciliation algorithm doesn't even run here.
In React, the output HTML is a result of the state and the props of your component.
When a change in state or props is detected, React runs the render method to check if it needs to update the DOM. So, in order to do this check, you need to store the parameters that determine your view in state or props.
Given your example, we could save the text you want to show in the p tag in the state of your component (using hooks):
import React, { useState } from 'react';
function App () {
const [text, setText] = useState('abc is here');
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => setText('abc is here') }>Btn</button>
</div>
);
}
}
export default App;
If you are using a version of React that does not support hooks, you will need to transform your functional component into a class to use state:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = { text: 'abc is here' };
}
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => this.setState({ text: 'abc is here' }) }>Btn</button>
</div>
);
}
}
export default App;

what am i doing wrong with ReactJS eventHandlers

I'm trying to make suggested search entries display in from Google Api appear in the div with the id Suggested-Places using input values from the input tag with the id SearchBar. Unfortunately,the event handlers aren't firing.
here is my code
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg';
import './App.css';
import MdShoppingCart from 'react-icons/lib/md/shopping-cart'
export default class HeaderMin extends Component{
constructor(props) {
super(props);
this.suggestedPlaces=[];
this.state={
suggestions:this.suggestedPlaces
}
this.userLocationInput=this.userLocationInput.bind(this);
this.suggestedLocations=this.suggestedLocations.bind(this);
}
componentDidMount() {
this.address=this.refs.inputBox.value;
const searchBar=ReactDOM.findDOMNode(this.refs.inputBox);
searchBar.addEventListener('keyUp keyPress keyDown',this.userLocationInput)
}
suggestedLocations(location){
this.suggestedPlaces.push(location);
}
userLocationInput() {
const key="&key=AIzaSyCvfy3g8ljGFtVyfCP9idWbwRo_-HASt_0",url="https://maps.googleapis.com/maps/api/place/textsearch/json?query=";
let query=this.address;
const endPoint=url+query+key;
return fetch("http://localhost:8080/"+url+query+key)
.then((res)=>res.json())
.then((res)=>res.results.map((loc)=>this.suggestedLocations(loc.formatted_address)))
}
render(){
return(
<div className="myheader header-min">
<img src="http://res.cloudinary.com/www-mybukka-com/image/upload/v1505151382/logo_m8ik1x.png" id="logo" alt="logo"/>
<div className="search-box search-box-min">
<div>
<input type='text' ref="inputBox" id="SearchBar" defaultValue='search your location'/>
<div id="Suggested-Places">{this.state.suggestions.map((location)=><p>{location}</p>)}</div>
</div>
<button className="btn-sml btn-red"></button>
</div>
<div className="header-top-button header-top-button-min">
<button ></button>
<button className="btn-red"></button>
<MdShoppingCart className="shopping-cart"/>
</div>
</div>
)
}
}
React use synthetic events, so your regular events won't probably work. Use the regular React way if you want it to work.
You should be using Reacts built-in event handler props. Also, you can simplify your code:
<input type='text' ref="inputBox" id="SearchBar" defaultValue='search your location' onKeyDown={this.userLocationInput}/>
You probably don't need to bind the same event to all 3 key events, onKeyDown should be enough. If you need the other ones as well, you can use onKeyPress and onKeyUp.
EDIT:
And after looking at userLocationInput, you'll need to make a small change:
userLocationInput(e) {
const key="&key=AIzaSyCvfy3g8ljGFtVyfCP9idWbwRo_-HASt_0",url="https://maps.googleapis.com/maps/api/place/textsearch/json?query=";
let query = e.target.value; // or this.refs.textInput.value
const endPoint=url+query+key;
return fetch("http://localhost:8080/"+url+query+key)
.then((res)=>res.json())
.then((res)=>res.results.map((loc)=>this.suggestedLocations(loc.formatted_address)))
}
When you bind this.address in the componentDidMount to this.refs.textInput.value, that's a one time assignment. this.address won't update everytime the value gets changed. So instead you should be using e.target.value or this.refs.textInput.value.
One last note, string refs are being deprecated in React so you should be using a ref callback instead.
<input ref={ ref => this.textInput = ref } />
...
// Getting value from input
this.textInput.value;

Having trouble using React Transition Group first time

Good day! I wrote this code to test the React transition group library and eventually get stuck with the error. The script gets run and I see the data fill form on the page styled but when I click submit button form does not disappear. Error reference description: Failed prop type: The prop timeout is marked as required in CSSTransition, but its value is undefined.
in CSSTransition (at app.jsx:24)
in App (at index.js:7) However transitionAppearTimeot={1500}!
import React, { Component } from 'react';
import CSSTransitionGroup from 'react-transition-group/CSSTransition';
import './app.css';
import Form from './components/Form';
class App extends Component {
constructor() {
super();
this.state = {
mounted: true,
};
this.handleSubmit = this.handleSubmit.bind(this);
};
handleSubmit(event) {
event.preventDefault();
this.setState = {
mounted: false
}
console.log(this.state);
};
render() {
return (
<div className="app">
<CSSTransitionGroup
transitionName="fade"
transitionAppear={true}
transitionAppearTimeout={1500}
transitionEnter={false}
transitionLeave={true}
transitionLeaveTimeout={300}>
{this.state.mounted && <Form onSubmit=
{this.handleSubmit} />}
</CSSTransitionGroup>
</div>
);
}
}
export default App;
The error message is very specific. <CSSTransition> requires a prop timeout but you do not pass anything. Also your other props are not what <CSSTransition> expects.
I guess you are mixing up the old react-transition-group v1 with the new react-transition-group v2. You are using v2 which has a totally different API.

Accessing refs in a stateful component doesn't work in React?

I'm currently trying to refactor the simple-todos tutorial for meteor using presentational and container components, but ran into a problem trying to access the refs of an input in a functional stateless component. I found out that to access refs, you have to wrap the component in a stateful component, which I did with the input.
// import React ...
import Input from './Input.jsx';
const AppWrapper = (props) => {
// ... more lines of code
<form className="new-task" onSubmit={props.handleSubmit}>
<Input />
</form>
}
import React, { Component } from 'react';
This Input should be stateful because it uses class syntax, at least I think.
export default class Input extends Component {
render() {
return (
<input
type="text"
ref="textInput"
placeholder="Type here to add more todos"
/>
)
}
}
I use refs to search for the input's value in the encompassing AppContainer.
import AppWrapper from '../ui/AppWrapper.js';
handleSubmit = (event) => {
event.preventDefault();
// find the text field via the React ref
console.log(ReactDOM.findDOMNode(this.refs.textInput));
const text = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
...
}
The result of the console.log is null, so is my Input component not stateful? Do I need to set a constructor that sets a value for this.state to make this component stateful, or should I just give up on using functional stateless components when I need to use refs?
or should I just give up on using functional stateless components when I need to use refs?
Yes. If components need to keep references to the elements they render, they are stateful.
Refs can be set with a "callback" function like so:
export default class Input extends Component {
render() {
// the ref is now accessable as this.textInput
alert(this.textInput.value)
return (
<input
type="text"
ref={node => this.textInput = node}
placeholder="Type here to add more todos"
/>
)
}
}
You have to use stateful components when using refs. In your handleSubmit event, you're calling 'this.refs' when the field is in a separate component.
To use refs, you add a ref to where you render AppWrapper, and AppWrapper itself must be stateful.
Here's an example:
AppWrapper - This is your form
class AppWrapper extends React.Component {
render() {
return (
<form
ref={f => this._form = f}
onSubmit={this.props.handleSubmit}>
<Input
name="textInput"
placeholder="Type here to add more todos" />
</form>
);
}
};
Input - This is a reusable textbox component
const Input = (props) => (
<input
type="text"
name={props.name}
className="textbox"
placeholder={props.placeholder}
/>
);
App - This is the container component
class App extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
const text = this._wrapperComponent._form.textInput.value;
console.log(text);
}
render() {
return (
<AppWrapper
handleSubmit={this.handleSubmit}
ref={r => this._wrapperComponent = r}
/>
);
}
}
http://codepen.io/jzmmm/pen/BzAqbk?editors=0011
As you can see, the Input component is stateless, and AppWrapper is stateful. You can now avoid using ReactDOM.findDOMNode, and directly access textInput. The input must have a name attribute to be referenced.
You could simplify this by moving the <form> tag into the App component. This will eliminate one ref.

React bootstrap - Uncaught TypeError: Cannot read property 'toUpperCase' of undefined

My class is as follows:
import React from 'react';
import Input from 'react-bootstrap';
export default class FormWidget1 extends React.Component {
render() {
if (!this.props.fields) {
console.log('no fields passed');
}
else {
console.log(this.props.fields.length);
console.log(this.props.fields);
}
var formFieldList = this.props.fields.map(function(field) {
console.log("iterating");
return (
<Input type="text" placeholder="testing" label="label" />
);
});
return (
<div>
<form action="">
{formFieldList}
</form>
</div>
);
}
}
If I replace <Input /> with <input /> then there's no error.
The stacktrace only shows my app.jsx which is not useful.
What is wrong?
You need to de-structure your import of the Input jsx component.
import {Input} from 'react-bootstrap';
What this does is render to var Input = require('react-bootstrap').Input; Whereas what you previously had would render to var Input = require('react-bootstrap');
It does mention this in the documentation for React Bootstrap here:
https://react-bootstrap.github.io/getting-started.html#es6
Edit: A good hint is that the error you're getting from React is a typical error when you're trying to render as a component, something which is actually not a react component. So basically you were trying to render the object that react bootstrap returns containing all components, rather than the actual input component you wanted.

Resources