React Refs are null after rerender - javascript

I'm new to React and I have the following react components that I'm using in a Blazor WASM App.
// Parent
export class Parent extends React.Component{
constructor(props){
super(props);
this.childRef = React.createRef();
// saving reference to component to access it using Blazor JS Interop
window.canvasComponentRef = this
}
render(){
return <Child ref={this.childRef} />
}
parentFoo = () => {
this.childRef.current.foo();
}
}
// Child
export class Child extends React.Component{
constructor(props){
super(props);
}
render(){
return <div> Content </div>
}
foo(){
// some actions in child
}
}
I render the component using...
ReactDOM.render(Parent, document.getElementById('root'));
Result: childRef.current work
When the user navigates away from the Parent component page, I unmount it manually using...
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
When the user comes back to the Parent component page, I render it again using...
ReactDOM.render(Parent, document.getElementById('root'));
Now, when I call window.canvasComponentRef.parentFoo(), childRef.current is null.
Can anyone explain why?
Thank you!

My issue was actually the global variable at
// saving reference to component to access it using Blazor JS Interop
window.canvasComponentRef = this
After refactoring it to get a ref to the Parent component using callback refs as below, the issue got resolved.
let parentRef = null;
function handleRef(element){
parentRef = element;
}
function renderParent(){
const parent = <Parent ref={this.handleRef}/>
ReactDOM.render(parent, document.getElementById('root'));
}
// Now call parent method like below:
function callParentFoo(){
parentRef.parentFoo();
}

Related

How to create an element and use its ref to append child elements in react?

I'm trying to create a html element on a parent component on react and access that component div inside a child component, then I can append new elements in the div from the child component.
After many attempts I wasn't able to fix props.canvasDivElement.current = null on the child constructor.
I've tried to do this using REF and without refs... No luck so far.
Any ideas?
The parent component looks like:
import React from "react";
import ReactViewer from "../ReactViewer/ReactViewer";
export default class CanvasWrapper extends React.Component {
private _divElement: React.RefObject<HTMLDivElement>;
constructor(props: any) {
super(props);
this._divElement = React.createRef<HTMLDivElement>();
}
render() {
return (
<div>
<ReactViewer canvasDivElement={this._divElement}></ReactViewer>
</div>
);
}
}
The child component:
import React from "react";
type ReactViewerState = {
};
type ReactViewerProps = {
canvasDivElement: React.RefObject<HTMLDivElement>;
};
export default class ReactViewer extends React.Component<ReactViewerProps, ReactViewerState> {
constructor(props: ReactViewerProps, state: ReactViewerState) {
super(props, state);
const newElement = document.createElement('span');
newElement.innerText = 'element';
props.canvasDivElement.current!.appendChild(newElement); //current is null
}
render() {
return (
<div ref={this.props.canvasDivElement} />
);
}
}
Rookie mistake, the element will always be null until it renders.
I've changed the line bellow to the componentDidMount event:
props.canvasDivElement.current!.appendChild(newElement);
It looks like you are trying to circumvent the basic principles of React to solve a problem that React provides other tools for. The React way of communicating between a child and a parent is to pass a callback function to the child. Then the child calls the callback to signal to the parent to update its children.

Pass parameter from external js to react prop

Trying to convert an in-app webpage to React. The app webview automatically calls a Javascript function to pass an access token to the webpage. So is it possible to use the same existing function to pass the token variable and store it as a React prop?
HTML
<div id="app"></div>
<script>
function setToken(token){
//set token to App token prop
}
</script>
JS
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
token: "",
}
}
render() {
return (
<div>
<p>{this.state.token}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
https://jsfiddle.net/wzovs2gy/
You could make it a callback function, so it will be given another function to call when it has a result (meaning it would be passed a function from within React that would then update the state/props. If you're looking to do this with a minimal number of changes, I would suggest that you have the function store the token in a variable, and then later query this variable in React.
It's impossible - or at least impractical - to access React component instance outside React application. This should be done in opposite way, the component should expose global function:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
token: "",
}
}
componentDidMount() {
window.setToken = token => {
this.setState({ token });
}
}
...
}
setToken shouldn't be called before React component initialization. Depending on where setToken is called, it may be beneficial to wrap the code that uses it with React application instead and avoid using globals.
Thanks for your help guys, managed to figure out a solution (while not elegant does the job). I declared the ReactDom.Render() as a variable which allows me to reference the function within the React Parent in the Vanilla JS.
JS
var ReactDom = ReactDOM.render(<App />, document.querySelector("#app"));
HTML
function setToken(token){
ReactDom.setNewToken(token);
}
https://jsfiddle.net/wzovs2gy/2
I have modified the fiddle:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
token: "test",
}
}
componentDidMount(){
setToken(this.state.token);
}
render() {
return (
<div>
<p>{this.state.token}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
https://jsfiddle.net/wzovs2gy/1/

Call component function from a JS File

I want to know If I can do this in react, I want to call a function or method of a react component from a JS file, so I could change the state of that component.
I have these three files for example
First App.js
import React,{Component} from 'react';
import Login from './Login';
class App extends Component{
constructor(){
super();
this.state = {session:false}
}
changeStateSession(state_session){
this.setState({session:state_session});
}
render(){
return(
this.state.session
?<div>Content</div>
:<Login/>
);
}
}
Login.js
import React, {Component} from 'react';
import Auth from './Auth.js';
class Login extends Component{
constructor(){
super();
}
login(){
Auth.login();
}
render(){
return(
<button onClick={(e)=>login(e)}></button>
);
}
}
And Auth.js
import App from './../../App.js';
const Auth = {
login:App.changeStateSession(true)
};
export default Auth;
What I really want to know is if theres a way that I could call the App function (changeStateSession) from the Auth.js file, the Auth.js file is just an example of what I would like to achieve I know this file doesn't work, but I would like to know If there is a way to achieve something like this in react, hope you can help me , thanks.
The more common way of doing something like this would be to pass your changeSessionState function as a prop to the Login component.
App.js:
class App extends Component {
constructor(props) {
super(props);
this.state = { session: false }
this.changeStateSession = this.changeStateSession.bind(this);
}
changeStateSession(stateSession){
this.setState({ session: stateSession });
}
render(){
return (
this.state.session
? <div>Content</div>
: <Login onSuccess={() => this.changeStateSession(true)} />
);
}
}
Login.js:
class Login extends Component {
constructor(props) {
super(props);
}
login(){
// If your login process is asynchronous
// and returns a Promise, for example
Auth.login()
.then(this.props.onSuccess);
}
render() {
return (
<button onClick={(e)=> this.login(e)}></button>
);
}
}
Now, when this.props.onSuccess is called once your login succeeds, your App component's state will be updated since your changeStateSession method was passed as a prop to your Login component.
The big takeaway here is that if you want to update a parent's state from a child component, passing functions from your parent component to the child component is the way to typically handle it. No other way of updating parent state from a child is recommended.
Also, if there is an absolute necessity to call the changeStateSession function from your Auth.js file, then it's a very similar concept. Just pass the function through and call it there instead.
You should probably use props. Either calling passing the function to a child component or just passing the value to the App component as a prop and calling the function in the parent component

How to pass an Array and access it within React Lifecycle

I'm using Mongo/Meteor 1.3/React. In my simple example I use an wrapper React component to query the Mongo collection and create an Array. When passing to the Child component, it seems like the Array object is not ready when constructor is called - meaning I can't access the props.
This feels like it must be a common problem. Should I be using a different React Lifecycle Component? Or adding some form of waitOn function? Any advice appreciated!!
Parent Component
export default class BulkMapWrapper extends TrackerReact(React.Component) {
constructor() {
super();
const subscription = Meteor.subscribe("listing",{sort: {_id:-1}})
this.state = {
eventsData: subscription
}
}
render () {
var markerArray = []
markerArray = ...
return(
<div className="panel panel-default">
<div className="panel-body">
<FourthMap
mapParams = {manyEvents}
markers = {markerArray}
/>
</div>
</div>
)
Child Component
export default class GooleMapComponent extends Component {
constructor(props){
super(props)
console.log(this.props.markers);
You should use the componentDidMount function to get the data and then set a new state with the resulting data.
class GetData extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
const subscription = Meteor.subscribe("listing",{sort: {_id:-1}});
this.setState({
eventsData: subscription
});
}
}
You can then pass down the state from the GetData component as props to its children or explicitly to another component in the render function.
This is generally how you should handle AJAX requests in React but I'm not sure if this will translate well to use in Meteor.

Letting a child know that it should update its state in React

I'm trying to let a child Component know that it should update its state after a prop change in the parent.
There is no state that needs to be shared between the two. The only thing that needs to happen is that the parent should somehow let the child know that it needs to update its state (literally call setState by itself with the info it already has).
So far I can only figure out to do in the "React"-way through the componentWillReceiveProps and sending some arbitrary props, like a number, to let the child know that it should call the function to set the state.
Another way would be to use signals to let the child know, but this seems a bit over the top for this situation.
So in summary:
The parent needs to let the child know that it should call a function
The function will update the state (setState) of the child
There is no need for the child to receive any information from the parent
Can anyone help me figure out the best way to do this?
As you can see in the snippet, this is more or less the situation. I would like to know the best way to have the Child component call the _updateState function when the Parents props have changed (does not happen in the snippet right now).
//Imagine this is the redux-container that passes the store state to the parent.
class ParentWrapper extends React.Component {
constructor(){
super();
this.state = {status: 'normal'};
}
//This would be an action to the reducer that would update the store state
_updateStatus(){
this.setState({status: 'updated'});
}
render(){
return (
<div>
<button onClick={this._updateStatus.bind(this)}>Click me</button>
<Parent status={this.state.status} />
</div>
);
}
}
class Parent extends React.Component {
render(){
return (
<div>
<Child />
</div>
);
}
}
Parent.propTypes = {
status: React.PropTypes.string
};
Parent.defaultProps = {
status: 'normal'
};
class Child extends React.Component {
constructor(){
super();
this.state = { test: 1 };
}
_updateState(){
this.setState({test: this.state.test + 1});
}
render(){
return (
<div>Child: {this.state.test}</div>
);
}
}
ReactDOM.render(<ParentWrapper />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
<div id="container"></div>
EDIT: added snippet.
You can use refs to access all the methods under the child component.
See the following fiddle
https://jsfiddle.net/pranesh_ravi/412j5ucw/
Here using refs, I'm calling a function inside the child which will change the state of the child component.

Categories

Resources