I have set up a React Frontend with a Node backend for an app I am trying to make. I have successfully created a server which is hosting my data, which I can then access and receive into my React Frontend. I am able to console.log the data I want and successfully saved it to the state (I think?). My issue is that I can't seem to actually pass the information contained in State into the child component.
Units.js
import UnitsCard from "./InfoCardUnits";
import React, { Component } from "react";
const axios = require("axios");
class Units extends Component {
constructor(props) {
super(props);
this.state = {
units: []
};
}
fetchData() {
axios
.get("http://localhost:3001/allData/units")
.then(response => {
// handle success
// console.log("Success");
this.setState({ units: response.data });
})
.catch(error => {
// handle error
console.error(error);
});
}
componentDidMount() {
this.fetchData();
}
render() {
// this console.log will show the data I want to send as props into my child component.
console.log(this.state.units[0]);
return <UnitsCard props={this.state.units[0]} />;
}
}
export default Units;
InfoUnitCard.js
import "../index.css";
function UnitsCard(props) {
// this console.log will show the "props" information that I want to use in my unit card. But the information itself won't actually show in the browser.
console.log(props);
return (
<div className="card">
<h2>{props.name}</h2>
<h2>{props.category}</h2>
<h2>{props.inf_melee}</h2>
</div>
);
}
export default UnitsCard;
When I console.log the state in either of the components it successfully shows the information I am trying to send. But I can't actually get that information to render. Any help or insights would be much appreciated.
EDIT: This has been resolved, thanks very much to everyone who chipped in an answer.
Avoid passing props in via the props keyword. Instead, consider making the following changes to your code:
render() {
// Get unit or empty object (makes code more readable in next step)
const unit = this.state.units[0] || {};
// Pass each piece of unit data in as a separate prop
return <UnitsCard
name={unit.name}
category={unit.category}
inf_melee={unit.inf_melee} />;
}
Alternatively, you could use the "spread" syntax available with ES6 to make this a little more concise:
render() {
// Get unit or empty object (makes code more readable in next step)
const unit = this.state.units[0] || {};
// Use spread operator to simplify passing of props to UnitsCard
return <UnitsCard {...unit} />;
}
Every thing you pass in the child component will be available in props object in the child component. In your case you are passing a 'props' to props object. This should be available as this.props.props.keyname. try changing your child component as follow.
function UnitsCard(props) {
// this console.log will show the "props" information that I want to use in my unit card. But the information itself won't actually show in the browser.
console.log(props);
return (
<div className="card">
<h2>{props.props.name}</h2>
<h2>{props.props.category}</h2>
<h2>{props.props.inf_melee}</h2>
</div>
);
}
You named your props props, so you can access to it with below code:
console.log(props.props);
you can pass like with a different name:
<UnitsCard child={this.state.units[0]} />
Then access to props with props.child, so your code will change to:
<h2>{props.child.name}</h2>
Related
I have a react frontend and most of my components are Class Based components, but it just so happens that one of the Components inside these class components has to be a functional component, so I currently have a functional component with a class based component.
Inside the functional component, I have a button that triggers a fetch call. After this fetch call is complete, I want the state of the class based (parent) component to update.
My approach was to make a function in the class based component (called setSubscriptoin) that adjusts the state of the class based component, then pass that function down to the functional component through props and call the function with a .then promise after the fetch call.
However, It appears that when I pass down the function through props, the functional component is not even able to detect the function and I get this error:
Uncaught (in promise) TypeError: e.setSubscription is not a function.
Here is the important code:
The class based component:
class OuterComponent extends React.Component {
constructor(props) {
super(props);
this.state = {subscription: {}}
}
setSubscription(subscription) {
this.setState({subscription: subscription})
}
render() {
return(
<Elements>
<FunctionComponent setSubscription={this.setSubscription.bind(this)}></FunctionComponent>
</Elements>
)
}
}
I wanted to include the elements part because I'm not sure if that could be effecting it. The FunctionComponent is wrapped inside a Stripe Elements provider. Not sure why that would do anything but I figured I should include it just in case.
The functional component:
const FunctionComponent = (props) => {
const fetchSomething = () => {
fetch('fetch is made here and is successful')
.then(response => {
if (some_condition) {
props.setSubscription({value1: 1, value2: 2}))
}
}
}
}
The problem is that the function component doesn't even recognize props.setSubscription as a function (as the error says).
I've tried console logging the props variable, and it does in fact have the function setSubscription in it so I have no clue what the issue could be. I've been trying to figure this out and am completely stumped. Does anyone know why this error is happening?
then should have a callback try this :
const FunctionComponent = (props) => {
const fetchSomething = () => {
fetch('fetch is made here and is successful')
.then(()=>props.setSubscription({value1: 1, value2: 2}))
}
}
I am developing a document verification system with ReactJS and solidity - smart contract. I want to display the result of my smart contract's get().call() method on the frontend, with a popup or even with a simple text.
My question is how can I do this? My .get().call() method seems to be working fine without any problem.
Check the image below, that's my code for now. I use console.log() to display the result.
If the console.log function displays your code in the console your codes work well, now you need to change the state by using this.setState function or useState hook to re-render the component. because in the ReactJS architecture, changing the state causes to change the interface. if your component is a class component:
import React, { Component } from 'react';
~~~
class YourComponentName extends Component {
constructor() {
super();
this.state = {
result: '',
~~~
~~~
};
}
onSubmitGet = async (event) => {
event.preventDefault();
cosnt hash = document.getElementById('hash').value;
await this.state.contract.methods
.get(hash)
.call({ form: this.state.address })
.then(res => this.setState({ result: res }))
};
~~~
render() {
const { result } = this.state;
return (
<>
<button onClick={this.onSubmitGet}>GET</button>
<div>{result}</div>
</>
);
}
};
The `~~~` means some other codes. actually, with using `setState` the `<div>{result}</div>` will change and show your result.
I have a parent component doing an AJAX call to get a JSON object. I've done a few console.log's to make sure that the data is correct in the parent component, but then when I pass through props, I get a value of:
ƒ data() {
return _this.state.data;
}
What I've done to this point seems simple enough so I can't find what the issue is.
Parent Component:
class InfoBox extends Component {
state = {
data: []
};
componentDidMount = () => {
this.loadDonationsFromServer();
setInterval(this.loadDonationsFromServer, this.props.pollInterval);
};
loadDonationsFromServer = () => {
$.ajax({
url: "https://jsonplaceholder.typicode.com/comments",
dataType: "json",
cache: false,
success: data => {
this.setState({ data });
},
error: (xhr, status, err) => {
console.error(status, err.toString());
}
});
};
render = () => {
return (
<React.Fragment>
<h1>Information</h1>
<InfoList
data={() => this.state.data}
/>
</React.Fragment>
);
};
}
export default DonationBox;
Child Component:
class InfoList extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data
};
}
componentDidMount() {
console.log(this.state.data);
//logs: ƒ data() {
// return _this.state.data;
// }
}
render() {
return <div> Placeholder </div>;
}
}
export default InfoList;
I tried using bind in the child component but still got the same thing:
constructor(props) {
super(props);
this.state = {
data: this.props.data
};
this.checkData = this.checkData.bind(this);
}
componentDidMount() {
this.checkData();
}
checkData = () => {
console.log(this.state.data);
};
First, yes, you should change the data prop that you send to InfoList to be this.state.data rather than an anonymous function. So: <InfoList data={this.state.data} />
But, the main issue is in using componentDidMount in the child component, when really you should be using componentWillReceiveProps instead.
componentDidMount is only called once, and it doesn't wait for your AJAX
The componentDidMount lifecycle hook is invoked one time, before the initial render.
In your child component, at componentDidMount you are trying to log this.state.data - but this state is based on what was set in the constructor which was what was passed in as the data prop when you first mounted InfoList. That was [], because InfoBox had yet to receive back data from its Ajax call. To put it another way:
InfoList.componentDidMount() fired before InfoBox.loadDonationsFromServer() got back its response. And InfoList.componentDidMount() does not get fired again.
componentWillReceiveProps is called whenever props change
Instead, your child component should be using the componentWillReceiveProps lifecycle hook. This is invoked every time a component receives new props. Once the parent's state changes (after load donations returns), then it passes new props to the child. In componentWillReceiveProps, the child can take these new props and updates his state.
I have created a code sandbox that shows you through a bunch of log statements what happens when, along with what your props and state look like at various points in the lifecycle. Instead of actually doing an ajax fetch, I'm just doing a 2-second wait to simulate the fetch. In InfoList.js, the code for componentWillReceiveProps is currently commented out; this way, you can see how things work as they stand. Once you remove the comments and start using componentWillReceiveProps, you'll see how they get fixed.
Additional resources
This is a helpful article that pretty much describes the exact same issue you're facing.
An excellent quick reference for React lifecycle hooks is the React Cheat Sheet)
That is because the data prop that is being passed in is a function.
Change
<InfoList data={() => this.state.data} />
to
<InfoList data={this.state.data} />
Just a nit, you don't really need the constructor in your child component to define the state. Just define it the way you have in your parent component.
Notes: using React for this.
Basically, I'm just trying to make a list of anchor elements from a list of links I have stored locally in a json file. I can confirm that the file is successfully seeing the "endpoints" data through console logs. However, the page just renders a white page and it doesn't look like the state is getting set correctly with the imported array.
So, this is what my file looks like right now (Any help would be greatly appreciated!):
import React from 'react';
import endpoints from './endpoints.json';
class Link extends React.Component{
constructor(props){
super(props);
this.state = {
error: null,
isLoaded: false,
myData: []
};
}
componentDidMount() {
let myData = endpoints.map((data, key) => {
console.log(endpoints);
console.log(endpoints[0].key);
return(
<a className="aLink" href={endpoints.link} key={endpoints.key} >{endpoints.name}</a>
)
})
this.setState({myData: myData});
console.log(this.state.myData);
}
render() {
const { error, isLoaded } = this.state;
if (error) {
return <div className="errorM">Error: {error.message}</div>;
} else {
return(
<div>
{this.state.myData}
</div>
)
}
}
}
export default Link;
You seem to be trying to render from the initial response (endpoints) rather than the map value (data). Change
href={endpoints.link} key={endpoints.key} >{endpoints.name}
to
href={data.link} key={data.key} >{data.name}
Well, this was one of those classic, ask a question and then immediately figure out the answer. Basically, where I'm mapping each item, I set an argument called "data". Instead of calling "endpoints.xxx" it should be "data.xxx" for everything. Then, everything renders fine. :)
I'm fairly new to react and struggle to update a custom component using componentDidMount and setState, which seems to be the recommended way of doing it. Below an example (includes an axios API call to get the data):
import React from 'react';
import {MyComponent} from 'my_component';
import axios from 'axios';
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
GetData() {
return axios.get('http://localhost:5000/<route>');
}
componentDidMount() {
this.GetData().then(
(resp) => {
this.setState(
{data: resp.data}
)
}
)
}
render() {
return (
<MyComponent data={this.state.data} />
);
}
}
Doing console.log(this.state.data) just below render() shows that this.state.data does indeed get updated (from [] to whatever the API returns). However, the problem appears to be that MyComponent isn't rendered afresh by componentDidMount. From the Facebook react docs:
Setting state in this method will trigger a re-rendering.
This does not seem to be the case here: The constructor of MyComponent only gets called once (where this.props.data = []) and the component does not get rendered again. I'd be great if someone could explain why this is and whether there's a solution or a different way altogether to get the updating done.
UPDATE
I've added the code for MyComponent (minus some irrelevant features, as indicated by ...). console.log(data_array) prints an empty array.
import React from 'react';
class DataWrapper {
constructor(data) {
this._data = data;
}
getSize() {
return this._data.length;
}
...
}
export class MyComponent extends React.Component {
constructor(props) {
super(props);
this._dataWrapper = new DataWrapper(this.props.data);
this.state = {
data_array: this._dataWrapper,
};
}
render() {
var {data_array} = this.state;
console.log(data_array);
return (
...
);
}
}
You are falling victim to this antipattern.
In MyComponent constructor, which only gets called the first time it mounts, passed your empty array through new DataWrapper and now you have some local state which will never be updated no matter what your parent does.
It's always better to have one source of truth, just one state object anywhere (especially for things like ajax responses), and pass those around via props. In fact this way, you can even write MyComponent as a simple function, instead of a class.
class Example extends Component {
state = { data: [] }
GetData() { .. }
componentDidMount() {
this.GetData().then(res =>
this.setState({data: new DataWrapper(res.data)})
)
}
render() { return <MyComponent data={this.state.data} /> }
}
...
function MyComponent (props) {
// props.data will update when your parent calls setState
// you can also call DataWrapper here if you need MyComponent specific wrapper
return (
<div>..</div>
)
}
In other words what azium is saying, is that you need to turn your receiving component into a controlled one. Meaning, it shouldn't have state at all. Use the props directly.
Yes, even turn it into a functional component. This helps you maintain in your mind that functional components generally don't have state (it's possible to put state in them but ... seperation of concerns).
If you need to edit state from that controlled component, provide the functions through props and define the functions in the "master" component. So the master component simply lends control to the children. They want anything they talk to the parent.
I'm not posting code here since the ammendment you need to make is negligible. Where you have this.state in the controlled component, change to this.props.