In my componentDidMount() I am making an API call "invokeservice" to fetch some data, this call then sets a state object that I use in my render.
The problem I am having is that the invokeservice is not being called and set the first time that the page is loaded, however when I refresh the page the invokeservice returns the data fine and the rest of the script will run. It's only the first time that it returns an null value.
Is it possible to call this invoke service in render function and keep the data ready and display it when user tries to load this page.
TRIED:
1.read that ComponentDidMount is called only after 1st initial render.
2. {this.invokeservice()} i tried to call in render function before return which did initial render but after 5sec it was blank and then again 5sec later it is filled again with values.
render function
public render() {
return (
<div className="monitor">
<div className="monitor__table">
{this.monitorrow(translate("cpu"), this.state.cpuString)}
{this.monitorrow(translate("memory"), this.state.pmemoryString)}
</div>
</div>
);
}
constructor
constructor(props) {
super(props);
this.state = {
cpu: 0,
cpuString: ""
};
setInterval(() => this.invokeservice(), 5000);
}
componentdidMount
public componentDidMount() {
this.invokeservice();
}
invokeservice
private invokeservice() {
var client = new HttpClient();
var url = this.props.baseUrl + '//getMonitorUsage' + '?NC=' + Date.now().toString();
client.get(url, (response) => {
this.setState({
cpu: JSONResponse.GetSystemStateResult.CPUusage
});
}
}
});
}
function
monitorrow(left,right) {
return (
<div className="table__row">
<div className="table__cell__left">
<Text>{left}</Text>
</div>
{ right &&
(<div className="table__cell__right">
<Text>{right}</Text>
</div>)
}
</div>
);
}
It is expected.
From the react docs:
These methods are called in the following order when an instance of a component is being created and inserted into the DOM:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
Ref: https://reactjs.org/docs/react-component.html
There is componentWillMount which will be called before render(). But is it not advised to be used. Read more on the official docs https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
Related
I'm studying chapter State and lifecycle in reactJS with Clock class and I don't understand why I can re-render my variables "myFirstNumber, mySecondNumber, myWord" which are not states when I use this code in CodePen:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date(),
};
this.mySecondNumber = 0;
}
componentDidMount() {
this.myFirstNumber = 0;
this.myWord = "Start";
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date(),
});
this.myFirstNumber += 1;
this.mySecondNumber += 1;
if (this.myFirstNumber ===5) {
this.myWord = "Finish";
}
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
<h2>myFirstNumber from ComponentDidMount: {this.myFirstNumber}</h2>
<h2>mySecondNumber from constructor: {this.mySecondNumber}</h2>
<h2>myWord: {this.myWord}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
The render method render all DOM each seconde for my variables which are not states ?
When you call this.setState function, the DOM gets rerendered. Here the DOM is getting updated every second when this.state.date gets changed through this.setState, showing new values. However, if you keep date outside of state, you can see that the DOM no longer rerenders, same result if you want to change "myWord" or "firstNumber" without changing anything in state.
When you run tick() the setState part is what triggers the re-render and that will process everything, state or not.
In componentDidMount() you schedule interval which triggers tick() function every second. Inside this function you update component's state by invoking setState() and passing current time as a new state value. Moreover, you modify some local variables of the class. When arguments passed to setState() are processed by JS engine, what happens asynchronously, not immediately after setState() is called, the component updates its state.
Then the render() function is called by the framework. The render() function returns output which reflects the current values of all variables requested inside render() function. If you didn't call setState() inside tick() method then you wouldn't see any changes even though you modify myFirstNumber and other variables after each second.
I have a component with a componentDidMount() method that calls a method called getData() which gets the initial data and sets the initial state of the component.
class LogsSettings extends React.Component {
constructor(props) {
super(props);
this.settingsUrls = [
"/ui/settings/logging"
];
this.state = {
configSettings: {},
formSchema: formSchema
};
this.configSettings = {};
this.selected = "general";
}
getData = (url, selectedSetting) => {
fetch(url)
.then((response) => {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
response.json().then((response) => {
//pass formschema here
console.log(selectedSetting);
let newFormSchema = this.setNonDefaultValues(response.data, formSchema.subsections);
Object.assign(this.configSettings, response.data);
this.setState({
configSettings : this.configSettings,
formSchema: newFormSchema
});
});
}
)
.catch((err) => {
console.log('Fetch Error :-S', err);
});
};
componentDidMount() {
this.settingsUrls.map((settingUrl) => {
this.getData(settingUrl, this.selected)
})
}
componentDidUpdate() {
this.settingsUrls.map((settingUrl) => {
this.getData(settingUrl, this.props.selectedSetting)
})
}
render() {
return (
<div className="card-wrapper">
<h2>{formSchema.label.toUpperCase()}</h2>
{
formSchema.subsections.map((subSection) => {
return (
<>
<h3>{subSection['description']}</h3>
<div style={{marginBottom: '10px'}}></div>
{
subSection['input_fields'].map((inputField) => {
return buildForm(inputField, this.handleChange)
})
}
<hr></hr>
</>
)
})
}
<button className="button button-primary">Save Changes</button>
</div>
)
}
}
The selectedSetting parameter that gets passed to the getData() method in this component will change however and when this changes, I need to change the state of the component and get new data specific to the changed selectedSetting parameter.
The new selectedSetting is passed into the component as a prop. The problem is that I can't pass the new selectedSetting parameter to my getData method to update the state of the component as it gets caught in an infinite loop.
How do I go about passing the new selectedSetting to the getData() method without getting caught in an infinite loop? Is this even possible? If not, what is the best approach I should take?
note the selectedSetting parameter isn't used in the getData() function yet but will be and it will be used to get data from an API call and a new form schema which will then lead to the ConfigSettings and formSchema states being changed
If you look closely on the lifecycle of your component, after mount, you'll fetch then update the component. This will trigger the componentDidUpdate lifecycle method which will do the same thing, causing the infinite loop. You need to have a flag that checks whether this.props.selected changed. If it didn't, don't fetch the data else fetch as normal. In the update method, you have access to the previous props. (You may also do this in componentShouldUpdate method, but it'll be just outright risky)
componentDidUpdate(prevProps) {
if( prevProps.selectedSetting !== this.props.selectedSetting ){
this.settingsUrls.map((settingUrl) => {
this.getData(settingUrl, this.props.selectedSetting)
})
}
}
also just a heads up, I noticed that your didMount method, uses a default of "general" as the selected setting, since you want to be using this.props.selectedSetting might be better if it was the one being used instead and just set default props to "general".
I'm creating a React Native Android App that pulls data from a database and displays it.
I would like to run a Javascript Function before the render() displays the variables.
as
render() {
return (
<View><Text>{ data }</Text></View>
);
}
Doesn't work because the variables aren't defined yet.
You can make use of componentDidMount function to call an api (if you want to call it only once) that returns you the data which you can save in the state and render
class App extends React.Component {
state = {
data: [],
loading: true
}
componentDidMount() {
ApiCall().then((data) => {
this.setState({data, loading: false})
})
}
render() {
if(this.state.loading) {
return 'Loading...'
}
return (
<View><Text>{this.state.data.map((obj => <View>{/* return that you want here from obj*/}</View>))}</Text></View>
);
}
}
To enhance UserExperience, you can have a loading state till your data is ready.
I need the template questionsCollected to call the getQuestions function when the template is rendered. I am able to call a function when an event is fired, but in this case I want the function to fire when the template is rendered and populate the options in the select menu.
The ajax call is successful and returns the option items. I can log the return to the console.
The shell of the template is successfully rendering to the page.
How do I call a function from within the template without utilizing an event (onClick, etc)?
Thanks!
class myClass extends React.Component {
constructor () {
super()
this.state = {
isActive: false,
}
this.getQuestions = this.getQuestions .bind(this)
}
getQuestions () {
const token = `xxxx`
const url = 'https://api.com' + token
Ajax.get(url).then(function (response) {
const data = JSON.parse(response.response)
const questions = []
Object.keys(data).forEach(function (value, key) {
questions.push(<option> + data.features[key].properties.question + </option>)
})
return questions
})
}
render () {
return <menuItems
children={this.renderChildren()}
/>
}
renderChildren () {
const questionsCollected = (
<div key='questionText' id='question'>
<select>
<option>Questions</option>
{this.getQuestions}
</select>
</div>
)
return [questionsCollected]
}
export default myContainer
Are you familiar with the react lifecycle? Check out this page, I refer to it often:
http://busypeoples.github.io/post/react-component-lifecycle/
I think you want to move the ajax call to componentDidMount. Upon its success you can call setState to set your questions from the ajax call in state, then your render method reads from the state object for the questions. Kind of like this:
https://daveceddia.com/ajax-requests-in-react/
Hope that helps
I have a react component that I wish to populate with images using the Dropbox api. The api part works fine, but the component is rendered before the data comes through & so the array is empty. How can I delay the rendering of the component until it has the data it needs?
var fileList = [];
var images = [];
var imageSource = [];
class Foo extends React.Component {
render(){
dbx.filesListFolder({path: ''})
.then(function(response) {
fileList=response.entries;
for(var i=0; i<fileList.length; i++){
imageSource.push(fileList[0].path_lower);
}
console.log(imageSource);
})
for(var a=0; a<imageSource.length; a++){
images.push(<img key={a} className='images'/>);
}
return (
<div className="folioWrapper">
{images}
</div>
);
}
}
Thanks for your help!
Changes:
1. Don't do the api call inside render method, use componentDidMount lifecycle method for that.
componentDidMount:
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here. If you
need to load data from a remote endpoint, this is a good place to
instantiate the network request. Setting state in this method will
trigger a re-rendering.
2. Define the imageSource variable in state array with initial value [], once you get the response update that using setState, it will automatically re-render the component with updated data.
3. Use the state array to generate the ui components in render method.
4. To hold the rendering until you didn't get the data, put the condition inside render method check the length of imageSource array if length is zero then return null.
Write it like this:
class Foo extends React.Component {
constructor(){
super();
this.state = {
imageSource: []
}
}
componentDidMount(){
dbx.filesListFolder({path: ''})
.then((response) => {
let fileList = response.entries;
this.setState({
imageSource: fileList
});
})
}
render(){
if(!this.state.imageSource.length)
return null;
let images = this.state.imageSource.map((el, i) => (
<img key={i} className='images' src={el.path_lower} />
))
return (
<div className="folioWrapper">
{images}
</div>
);
}
}
You should be using your component's state or props so that it will re-render when data is updated. The call to Dropbox should be done outside of the render method or else you'll be hitting the API every time the component re-renders. Here's an example of what you could do.
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
imageSource: []
}
}
componentDidMount() {
dbx.filesListFolder({ path: '' }).then(function(response) {
const fileList = response.entries;
this.setState({
imageSource: fileList.map(file => file.path_lower);
})
});
}
render() {
return (
<div className="folioWrapper">
{this.state.imageSource.map((image, i) => <img key={i} className="images" src={image} />)}
</div>
);
}
}
If there are no images yet, it'll just render an empty div this way.
First off, you should be using the component's state and not using globally defined variables.
So to avoid showing the component with an empty array of images, you'll need to apply a conditional "loading" class on the component and remove it when the array is no longer empty.