Pass parameter from external js to react prop - javascript

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/

Related

React Refs are null after rerender

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();
}

ReactJS state changes unexpectedly

I am working on a user interface in React.
I am currently facing an issue which I do not really know how to debug.
Somehow the state of a component changes unexpectedly. There is no code on my part which changes the state variable however.
The mutation happens when pressing a button, but I have literally written no code which modifies this state variable. Is there a way in React to see who/which functions made changes to the state? How would one debug this kind of scenario?
You've referred to a "state variable" in your question, but the only code shown in your question uses props, not state.
Props are controlled by the parent element, which can change them even after the component has been created. Here's an example:
class Example extends React.Component {
constructor(props) {
super(props);
console.log("constructor");
}
render() {
console.log("render");
return <div>{this.props.childprop}</div>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0};
}
componentDidMount() {
const handle = setInterval(() => {
this.setState(({value}) => ({value: value + 1}));
}, 500);
this.cleanup = () => {
clearInterval(handle);
};
setTimeout(this.cleanup, 2000);
}
componentWillUnmount() {
if (this.cleanup) {
this.cleanup();
}
}
render() {
const {value} = this.state;
return <Example childprop={value} />;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
Notice how App's code changes the value of the childProp in Example, after the Example component has been created. Doing so makes the component re-render.
So code in an event handler can easily see an updated prop value, if the parent has changed it.
Ciao, a way to intercept if a component is receiving props update is componentWillReceiveProps() function (now is called UNSAFE_componentWillReceiveProps()). A way to intercept if a component is chaning his state is componentWillUpdate() function (now is called UNSAFE_componentWillUpdate()), Try to use these to debug your application (maybe you will see some unexpected behaviour that could help to understad the cause of your problem).

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

ReactJS: Updating parent application state from child component

Currently trying to learn React by making a simple application that grabs data from the openFEC API.
I currently have two components defined in my application, a SearchBar and a Candidate component. This is what my App.js currently looks like:
class App extends Component {
constructor(props){
super(props);
this.state = { candidate: [], searchTerm: '' }
}
render() {
return (
<div className="App">
<SearchBar />
<Candidate candidate={this.state.candidate}/>
</div>
);
}
}
export default App;
Problem: I need to update the Candidate component based on the data I receive from the API response. However, I'm making the API call in the SearchBar component and have no way of updating the candidate state defined in the App component.
Should I make the API call in the App component instead? If not, is there a way to send the data I get back from the SearchBar component into my App component?
I think the best way to do this is have the API call in your App Component, and pass that function down as a prop to your SearchBar Component. Your parent component (in this case, App) should be holding on to all of the relevant information and passing down to it's children what they need.
It should look something like this:
class App extends Component {
...
handleSearch(term) {
//handle fetch here
.then(res => this.setState({candidate: res})
}
render() {
<div className="App">
<SearchBar handleSearch={this.handleSearch}/>
<Candidate candidate={this.state.candidate}/>
</div>
}
}
In this way, you can achieve this
class App extends Component {
constructor(props){
super(props);
this.state = { candidate: [], searchTerm: '' }
this.triggerSearch=this.triggerSearch.bind(this);
}
triggerSearch(searchTerm){
this.setState({searchTerm})
}
render() {
return (
<div className="App">
<SearchBar trigerSearch=
{(searchTerm)=>this.triggerSearch(searchTerm)} />
<Candidate candidate={this.state.candidate}/>
</div>
);
}
}
export default App;
You can achieve it this way (without making API call from App).
class App extends Component {
constructor(props){
super(props);
this.state = { candidate: [], searchTerm: '' }
this.onDataReceived = this.onDataReceived.bind(this);
}
onDataReceived(data){
this.setState({ candidate: data });
}
render() {
return (
<div className="App">
<SearchBar onDataReceived={this.onDataReceived}/>
<Candidate candidate={this.state.candidate}/>
</div>
);
}
}
Roughly what happens here is:
You can see how I passed a function as a props to the SearchBar component via onDataReceived props.
You can invoke that function from within SearchBar component (e.g. make API call and call function passed as props with API results).
Invoking onDataReceived function will trigger setState
Calling setState will call render and now the Candidate component will receive more recent data from state.
More.

Call class method of default import in react

I'm wondering whether its possible to call a method on a component that I import from another file. Basically, my situation is that I have two react classes. One of them is a Sudoku puzzle, which I call Game, and which includes the updateArray() method:
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {arr: [[5,0,4,9,0,0,0,0,2],
[9,0,0,0,0,2,8,0,0],
[0,0,6,7,0,0,0,0,9],
[0,0,5,0,0,6,0,0,3],
[3,0,0,0,7,0,0,0,1],
[4,0,0,1,0,0,9,0,0],
[2,0,0,0,0,9,7,0,0],
[0,0,8,4,0,0,0,0,6],
[6,0,0,0,0,3,4,0,8]]};
this.handleSubmit = this.handleSubmit.bind(this);
this.updateArray = this.updateArray.bind(this);
}
componentWillReceiveProps(nextProps) {
if(nextProps.arr != this.props.arr){
this.setState({arr: nextProps.value });
}
}
updateArray(str_arr) {
this.setState({arr: str_arr});
}
handleSubmit(event) {
...
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className = "game">
<div className = "game-board">
<Board value = {this.state.arr} />
</div>
<div className = "game-info">
<div></div>
</div>
</div>
<input type="submit" value="Submit" />
</form>
);
}
}
export default Game;
And then I have a second class that gets a image of a sudoku puzzle and makes a corresponding 9x9 array using computer vision methods. I then try to send the array back to Game using its updateArray function:
import Game from './Sudoku';
export default class ImageInput extends React.Component {
constructor(props) {
super(props);
this.state = {
uploadedFile: ''
};
}
onImageDrop(files) {
this.setState({uploadedFile: files[0]});
this.handleImageUpload(files[0]);
}
handleImageUpload(file) {
var upload = request.post('/')
.field('file', file)
upload.end((err, response) => {
if (err) {
console.error(err);
}
else {
console.log(response);
console.log(Game);
//ERROR HAPPENING HERE
Game.updateArray(response.text);
}
});
}
render() {
return (
<div>
<Dropzone
multiple = {false}
accept = "image/jpg, image/png"
onDrop={this.onImageDrop.bind(this)}>
<p>Drop an image or click to select file to upload</p>
</Dropzone>
);
}
}
However, when I try to send the array to Game's method, I get a Uncaught TypeError:
Uncaught TypeError: _Sudoku2.default.updateArray is not a function
at eval (image_input.js?8ad4:43)
at Request.callback (client.js?8e7e:609)
at Request.eval (client.js?8e7e:436)
at Request.Emitter.emit (index.js?5abe:133)
at XMLHttpRequest.xhr.onreadystatechange (client.js?8e7e:703)
I want the updateArray() method to update the Game from a separate file, which will then cause the Game to re-render. Is this possible? I've spent a lot of time reading documentation, and it seems as though what I'm suggesting is not the typical workflow of react. Is it dangerous, and if so, can someone explain why?
Also, both classes are rendered in a separate file that looks like this:
import Game from './Sudoku';
import ImageUpload from './image_input';
document.addEventListener('DOMContentLoaded', function() {
ReactDOM.render(
React.createElement(ImageUpload),
document.getElementById('image-upload'),
);
ReactDOM.render(
React.createElement(Game),
document.getElementById('sudoku_game'),
);
});
First of all, in your separate file (the one rendering both Game and ImageInput components):
Make it render only one component. This could have a original name like App for instance. Like this:
import App from './App';
document.addEventListener('DOMContentLoaded', function() {
ReactDOM.render(
React.createElement(App),
document.getElementById('root'),
);
});
You would only have to change the imports and name of the root element as needed of course.
Then, for the App component:
import React from 'react';
import Game from './Sudoku';
import ImageUpload from './image_input';
class App extends React.Component {
constructor() {
super();
this.state = {
sudokuArray = [];
}
}
updateArray(newArray) {
this.setState({sudokuArray: newArray})
}
render() {
<div>
<Game sudokuArray={this.state.sudokuArray} />
<ImageUpload updateArray={this.updateArray.bind(this)} />
</div>
}
}
export default App;
And inside your ImageInput component you would call the update method like:
this.props.updateArray(response.text).
Also, inside your Game component, change the render function, specifically the part with the Board component to: <Board value = {this.props.sudokuArray} />.
This is a rather common situation when you are learning React. You find yourself trying to pass some prop or run some method inside a component that is not "below" the component you are currently working with. In these cases, maybe the prop you want to pass or the method you want to run should belong to a parent component. Which is what I suggested with my answer. You could also make Game as a child of ImageInput or vice-versa.

Categories

Resources