ReactJS update UI or call method from different class - javascript

I have two classes, App and qrCode. The qrCode class returns a image of the qrcode based on the address provided in that classes state. The app class displays the qrcode component. In the method getSessionID, the app makes a request to the server, then should call updateQrCode to update the qrcode that is being shown.
How can I call the method updateQrCode from App.js? I can't instantiate a new QrCode, because that is not the instance that is being displayed.
App.js:
import React, { Component } from "react";
import "./App.css";
import QrCode from "../qrCode/qrCode";
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="App">
<header className="App-header">
<QrCode />
</header>
</div>
);
}
getSessionID() {
fetch("/getSessionID")
.then((res) => {
// code here to update the qr code in the instance of QrCode
// QrCode.updateQrCode("test")
})
.then((data) => console.log(data.message));
}
}
export default App;
QrCode.js:
import "./qrCode.css";
import QRCode from "qrcode";
import React, { Component } from "react";
class QrCode extends Component {
constructor(props) {
super(props);
this.state = {
qrCode: {
address: null,
image: null,
},
};
}
componentDidMount() {
generateQrCode(this.state.qrCode.address).then(
function (image) {
this.setState({
qrCode: {
address: this.state.qrCode.address,
image: image,
},
});
}.bind(this)
);
}
updateQrCode = () => {
this.setState({
qrCode: {
address: "asdf",
image: null,
},
});
};
render() {
return (
<div>
<img src={this.state.qrCode.image} alt="QR Code"></img>
</div>
);
}
}
function generateQrCode(address) {
return new Promise(function (resolve, reject) {
if (address) {
QRCode.toDataURL(address, {
errorCorrectionLevel: "h",
color: {
dark: "#000000",
light: "#0000",
},
})
.then((dataURI) => {
resolve(dataURI);
})
.catch((err) => {
console.error(err);
reject(err);
});
} else {
return address;
}
});
}
export default QrCode;

App.js is the parent component and QrCode.js is the child component. From parent to child you can pass by props directly.
In App.js, create a state object and update it on receiving fetch response. React re-renders component and its children whenever state object changes.
App.js
import React, { Component } from "react";
import "./App.css";
import QrCode from "../qrCode/qrCode";
class App extends Component {
constructor(props) {
super(props);
this.state = { qrCodeImage: null};
}
render() {
return (
<div className="App">
<header className="App-header">
<QrCode qrCodeImage={this.state.qrCodeImage} />
</header>
</div>
);
}
getSessionID() {
fetch("/getSessionID")
.then((res) => {
// code here to update the qr code in the instance of QrCode
// QrCode.updateQrCode("test")
})
.then((data) => this.setState({ qrCodeImage:data }));
}
}
export default App;
QrCode.js
updateQrCode = () => {
this.setState({
qrCode: {
address: "asdf",
image: this.props.qrCodeImage,
},
});
};

Related

My React function being passed down as props is not being invoked

I am trying to re-render the page based on a button click. I have the function updateCowList which calls setState() in my app component. The handleClick logic is in my newCow component which handles the button and the text input.
The console.logs() that I am seeing are 'fire', but I am not seeing the 'after' console.log(), nor am I seeing any of the logs within my updateCowList function in App.
How can I get my updateCowList function to run? I have tried calling it in all sorts of ways, destructuring props, etc.
Here is my App:
import React from 'react';
import CowList from './CowList.jsx';
import CowListEntry from './CowListEntry.jsx';
import axios from 'axios';
import SearchDB from './searchDB.js';
import NewCow from './NewCow.jsx';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
cows: []
}
// this.updateCowList = this.updateCowList.bind(this);
}
componentDidMount() {
SearchDB()
.then((res) => {
this.setState({cows: res.data})
}, (err) => {
console.log(err);
});
}
updateCowList(cow) {
console.log('update cow list is running')
oldCows = [...this.state.cows];
newCows = oldCows.push(cow);
console.log('new cows be4 set state', newCows);
this.setState({cows: newCows});
console.log('new cows after set state', newCows);
}
render() {
return (
<div>
<CowList cows={this.state.cows}/>
<NewCow props={this.updateCowList}/>
</div>
)
}
}
export default App;
here is my NewCow component:
import React from 'react';
import axios from 'axios';
class NewCow extends React.Component {
constructor(props) {
super(props);
this.state = {
entry: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
let split = this.state.entry.split(', ')
console.log(split)
axios.post('http://localhost:3000/api/cows', {
name: split[0],
description: split[1]
})
.then(res => { console.log('fire', res.data);
this.props.updateCowList(res.data);
console.log('after')
})
.catch(err => 'error submitting cow :( mooooo');
}
handleChange (event) {
this.setState({entry: event.target.value})
}
render () {
return (
<div className='newCowForm'>
<input className='form-control' type='text' onChange={this.handleChange} value={this.state.entry} placeholder={'name, description'} />
<button onClick={this.handleClick} className='newCowButton'>Create new cow</button>
</div>
)
}
}
export default NewCow;
<NewCow props={this.updateCowList}/>
should be :
<NewCow updateCowList={this.updateCowList}/>

State is not passed down to child component

I have an issue in figuring in what conditions props are not passed down by the tree. I have a Fetcher class in which I populate with "layouts", then pass it to children props, but I cannot access it from child component.
EX:
import React, { Component } from 'react'
import axios from "axios";
export default class Fetcher extends Component {
constructor(props) {
super(props)
this.state = {
layouts: [],
}
componentDidMount() {
this.getLayouts();
}
getLayouts = () => {
axios
.get("/layout")
.then((res) => {
this.setState({
layouts: res.data,
});
})
.catch((err) => console.log(err));
};
render() {
return (
this.props.children(this.state.layouts)
)
}
}
This is my Parent component on which I pass some props children:
ex:
import React, { Fragment } from "react";
import Fetcher from "./Fetcher";
class App extends Component {
<Fetcher>
{(layouts) => {
return <Fragment>
<NewLayout
layoutsList={layouts} />
</Fragment>
}}
</Fetcher>
}
import React from "react";
class NewLayout extends React.Component {
constructor(props) {
super(props)
this.state = {
layouts: [],
}}
componentDidMount() {
this.setState(() => ({
layouts: this.props.layoutList
}))
}
render() {
{ console.log(this.state.layouts) }
{ console.log(this.props.layoutList) }
return (
....
The children prop is not a function, if you want to pass a property to it you should use React.Children API with React.cloneElement:
class Fetcher extends Component {
state = {
layouts: [/*some layout values*/],
};
render() {
const children = this.props.children;
const layouts = this.state.layouts;
return React.Children.map(children, (child) =>
React.cloneElement(child, { layouts })
);
}
}
Typo my friend, looks like you pass layoutsList prop to NewLayout, but internally use layoutList.

How to get state of _app.js in Next.js?

I have initiated a state in _app.js using Next.js.
I would like to use this state in the index.js file.
How can I access it?
This is my _app.js code:
import React from 'react';
import App, { Container } from 'next/app';
import Layout from '../components/Layout';
export default class MyApp extends App {
constructor(props) {
super(props);
this.state = {
currencyType: {
name: 'Ether',
price: 1,
},
ethPriceUsd: 1,
};
}
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
let ethPriceUsd;
if (Component.getInitialProps) {
fetch(`https://api.coingecko.com/api/v3/coins/ethereum/`)
.then((result) => result.json())
.then((data) => {
ethPriceUsd = parseFloat(data.market_data.current_price.usd).toFixed(
2
);
});
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps, ethPriceUsd };
}
componentDidMount() {
const ethPriceUsd = this.props.ethPriceUsd;
this.setState({ ethPriceUsd });
}
onCurrencyTypeChange(currencyTypeValue) {
let currencyType = {};
//Value comes from Header.js where Ether is 0 and USD is 1
if (currencyTypeValue) {
currencyType = {
name: 'USD',
price: this.state.ethPriceUsd,
};
} else {
currencyType = {
name: 'Ether',
price: 1,
};
}
alert('We pass argument from Child to Parent: ' + currencyType.price);
this.setState({ currencyType });
}
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Layout changeCurrencyType={this.onCurrencyTypeChange.bind(this)}>
<Component {...pageProps} />
</Layout>
</Container>
);
}
}
A lot of it is irrelevant (Like passing the data to the Layout etc...). All I want to do is use this state in my index.js.
let's say you have this code in _app.js.
import React from 'react'
import App, { Container } from 'next/app'
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
state = {
name: "Morgan",
}
render () {
const { Component, pageProps } = this.props
return (
<Container>
<Component {...pageProps} {...this.state}/>
</Container>
)
}
}
Please notice the state and <Component {...pageProps} {...this.state}/>
Solution 1:
Now, let's see how can we use it in index.js or any other pages
import React from 'react';
export default class Index extends React.Component {
render() {
return (
<div>
<h2>My name is {this.props.name}</h2>
</div>
)
}
}
You can use them as props like this this.props.name
Solution 2:
Populate state in the index.js from props and then access it from state
import React from 'react';
export default class Index extends React.Component {
constructor(props) {
super(props)
this.state ={
name: this.props.name
}
}
render() {
return (
<div>
<h2>My name is {this.state.name}</h2>
</div>
)
}
}
You can use them as props like this this.state.name

Render whole html file in react component

I am serving some content from my API.
I want display response from API in my react component.
Response is html with bundled all assets inline by webpack.
How can I do it?
I tried dangerouslySetInnerHTML but it crashes my javascript inside returned html.
My cmp :
import React, { Component } from 'react';
import axios from 'axios';
export default class Report extends Component {
constructor() {
super();
this.state = {
id: null,
report: null
};
}
getParam(param){
return new URLSearchParams(window.location.search).get(param);
}
componentWillMount() {
axios.post(`/url`,
{
'id': this.getParam('id'),
}
)
.then(res => {
this.setState({id: res.data});
setTimeout(() => {
axios.get(`https://rg.ovh/`+this.state.id)
.then(res => {
this.setState({report: res.data})
});
}, 1900);
});
}
render() {
return (
<div dangerouslySetInnerHTML={ {__html: this.state.report} } />
);
}
}
import axios from 'axios';
import React, { Component } from 'react';
import renderHTML from 'react-render-html';
class App extends Component {
constructor() {
super();
this.state = {
htmlString: ''
};
}
componentDidMount() {
axios.get('http://localhost:5000').then(response => {
this.setState({ htmlString: response.data })
}).catch(err => {
console.warn(err);
});
}
render() {
const { htmlString } = this.state;
return (
<div className="App">
{renderHTML(htmlString)}
</div>
);
}
}
export default App;

Setting response from axios to state

When I try to setState after the get axios request it doesn't seem that I have the data ready to render.In console, I am getting a response but unable to access the response in the state.
import React, { Component } from 'react';
import axios from 'axios';
import { Grid, Row, Col } from 'react-flexbox-grid';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import stylefile from './stylefile';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import { withStyles } from '#material-ui/core/styles';
import '../App.css';
class TitleBar extends Component {
constructor() {
super();
this.state ={
data:[],
}
}
componentDidMount() {
axios.get('http://api.abc',
{
headers: { "Postman-Token": "abc"}
})
.then((response) => {
console.log(response.data);
this.setState({
data: response.data,
})
})
.catch((error) => {
console.log(error)
})
}
render() {
const { classes } = this.props;
console.log(this.state.data,"data response")
return (
<div>
{
this.state.data.map(((item,key) => (
<div>
//
</div>
)))}
</div>
);
}
}
export default withStyles(stylefile)(TitleBar);
console.log(error);
});
}
//console.log(this.state.data) -- is undefined
Your API response object includes an object like:
const response = {
data: {
MESSAGE: "List details Fetch successful",
STATUS: "SUCCESS",
DATA: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" }
],
HASH: "3--0"
}
};
So, you need response.data.DATA for your state here:
this.setState( { data: response.data.DATA } );
Here is a working example of mimicking your situation.
const remoteData = {
data: {
MESSAGE: "List details Fetch successful",
STATUS: "SUCCESS",
DATA: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" },
],
HASH: "3--0",
},
};
const fakeRequest = () =>
new Promise( resolve => setTimeout( () => resolve( remoteData ), 1000 ) );
class App extends React.Component {
state = {
data: [],
};
componentDidMount() {
fakeRequest().then( response => this.setState( { data: response.data.DATA } ) );
}
render() {
return (
<div>
{this.state.data.map( el => (
<div key={el.id}>
<p>{el.id}</p>
<p>{el.name}</p>
</div>
) )}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById( "root" )
);
<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>
<div id="root"></div>
From React official docs,
componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering
Also,one should use componentDidMount as componentWillMount is deprecated in new version of react.
componentDidMount() {
axios.get('http://api./abc',
{
headers: { "Postman-Token": "abc" }
})
.then((response) => { //use arrow to get setState on this call without any extra binding or placeholder variable
console.log(response.data);
this.setState({
data: response.data,
})
})
.catch((error) => {
console.log(error)
})
}
Try to fix those lines of code:
constructor(props) { //fixed
super(props); //fixed
this.state ={
data:[],
}
}
This is just the way ReactJS set up the constructor() method for a class component. We just obey the React's rules while working with it.
From the Official React Document, they said:
The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.
For more information about the contructor() method: https://reactjs.org/docs/react-component.html#constructor
This is a working example which I've already made just for you as a reference.
The demo is now available on CodeSandBox: https://codesandbox.io/s/8xvn8yl1l2
TitleBar.js
import React, { Component } from 'react';
import axios from 'axios';
export default class TitleBar extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
console.log(res.data);
this.setState({ data: res.data });
})
.catch(err => console.log(err.message));
}
render() {
return (
<div>
{this.state.data.map(i => (
<div key={i.id}>
<h2>{i.title}</h2>
</div>
))}
</div>
);
}
}
App.js
import React, { Component } from 'react';
import TitleBar from './components/TitleBar';
class App extends Component {
render() {
return (
<div>
<TitleBar></TitleBar>
</div>
);
}
}
export default App;
By following this example, if the this.state.data is still undefine, then there are two things that we could focus on in order to successfully debug:
1.The structure of the response data object. In your case, the solution could be
this.setState({
data: response.data.DATA
})
2.Does the API work as expected.
Hopefully that helps.

Categories

Resources