ReactJs get the state of child component - javascript

I want to use the state of my child Component in my parent component in this example . what i want exactly to be able to do is this:
<Application onChange={(myVar)=>{console.log(myVar)}}/>

You're onChange is just a normal prop which you pass into your Application component. So if it's a function you can call it whenever you want. In this case, it makes more sense to call it when Application's state is updated. You can use the second parameter of setState function which is an optional callback function for this. But just make sure you onChange prop is defined and it's a function before you call it with any parameter which you want to pass.
class Application extends React.Component {
constructor(props){
super(props);
this.state = {myVar:0};
setInterval(()=>{
this.setState( { myVar:this.state.myVar + 1 }, () => {
if(this.props.onChange && typeof this.props.onChange === "function")
this.props.onChange(this.state.myVar)
} );
}, 3000);
}
//.....
}
Updated Codepen: https://codepen.io/anon/pen/gWvKxa

Related

ReactJS ComponentWillMount() after passing Props

I want to ask why the child ( ComponentWillMount() ) component is only once rendered, once I am passing props to it everytime on onClick.
Once I click some button that is passing props to the child, the ComponentWillMount() of child is not triggering again, only in the first click only.
Parent Component:
render(){
return(
<div>
<AppModuleForm
editID = {this.state.editID}
editURL = {this.state.editURL}
editConf = {this.state.editConf}
editDesc = {this.state.editDesc}
editIcon = {this.state.editIcon}
editParent = {this.state.editParent}
editOrder= {this.state.editOrder}
status={this.state.status}
moduleList={this.state.moduleList}
updateAppModuleTree={this.updateAppModuleTree.bind(this)}/>
</div>
)
}
Child Component:
constructor(props){
super(props)
console.log(this.props.editDesc)
this.state={
url:'',
description:'',
parentID:'',
order:'',
configuration:'',
icon:'',
parentIDList:[],
urlDuplicate: false,
isSuccess: false,
errorMessage: '',
}
}
componentWillMount(){
if(this.props.status==='edit'){
let {
editURL,
editDesc,
editParent,
editConf,
editIcon,
editOrder} = this.props
this.setState({
url:editURL,
description:editDesc,
parentID:editParent,
order:editOrder,
configuration:editConf,
icon:editIcon,
})
}
}
componentWillReceiveProps(nextProps){
if(nextProps.status != this.props.status){
if(this.props.status==='edit'){
let {
editURL,
editDesc,
editParent,
editConf,
editIcon,
editOrder} = this.props
this.setState({
url:editURL,
description:editDesc,
parentID:editParent,
order:editOrder,
configuration:editConf,
icon:editIcon,
})
}
}
}
ComponentWillMount is mounting lifecycle method which will be called before mounting your component hence initialisation can be done in that while ComponentWillReceiveProps will be called once props are changed and you will get changes in nextProps parameter.
First you need to read https://reactjs.org/docs/state-and-lifecycle.html
and understand where to use props and why you need to pass something into component state.
From http://lucybain.com/blog/2016/react-state-vs-pros/
So when would you use state?
When a component needs to keep track of information between renderings
the component itself can create, update, and use state.
So you shouldn't transfer to state anything that will not change internally during component live cycle. As I can see all props those you pass to component are most likely will not be changed from within the component, all callbacks and icons you should take from props in component jsx.
If you have some editable data that you pass into its props from parent, on component mount (use componentWillMount()) you can copy that data to component state.That means all data will be stored in component internally and will not being overwritten on every render() call from passed props.
If you need to check if new props contains changes you can use componentWillReceiveProps(newProps) and there you can compare newProps with this.props and and process changes if needed.
Also i can suggest you to rename component callbacks handlers with respect to naming best practices:
<div>
<AppModuleForm
handleEditID = {this.onEditID}
handleEditURL = {this.onEditURL}
handleEditConf = {this.onEditConf}
handleEditDesc = {this.onEditDesc}
handleEditIcon = {this.onEditIcon}
handleEditParent = {this.onEditParent}
handleEditOrder= {this.onEditOrder}
status={this.state.status}
moduleList={this.state.moduleList}
updateAppModuleTree={this.updateAppModuleTree.bind(this)}/>
</div>
And I dont see any reasonable purpose to declare or to store functions in components state. So you can consider to move your handlers this.state.editID
etc. to parent component this scope. Like that
onEditId = () => { /* function code */ }
If you use arrow function = () => it automatically binds to component this and you don't need to bind them manually like you do in
{this.updateAppModuleTree.bind(this)}
After all that may be you will understand more clearly how you should manage your components life cycle and your problem will no longer be relevant.

Unable to use setState in react component

I have the following code that is not letting me set the state ever after getting mounted.
Here is the code
import React, { Component } from 'react';
import Messages from '../locale/en/Messages';
import '../styles/base.css';
class AlertService extends Component {
state = {
message: '',
classType: 'alert-info',
isMessageSet: false
}
Messages = new Messages();
componentDidMount = () => {
console.log('This has mounted'); // This is working
}
componentWillUnmount = () => {
console.log('Is this getting unounted ?'); // This is working, the component is not getting unmounted
}
setAlert = (key, type, isMessage, readMore) => {
let message = isMessage ? key : this.Messages[key];
let classType = 'alert-info';
if (type === 0) {
classType = 'alert-danger';
} else if (type === 1) {
classType = 'alert-success';
}
this.openMessage(message,classType);
}
openMessage = (message,classType) =>{
this.setState({
message: message,
classType: classType,
isMessageSet: true
});
}
closeMessage = () => {
this.setState({
message: '',
classType: 'info',
isMessageSet: false
});
}
render() {
let classes = this.state.classType + ' ' + 'alertBox';
return (this.state.isMessageSet ?
<div className={classes}>
<div className="col-md-11"> {this.state.message} </div>
<div className="col-md-1 closeAlert" onClick={this.closeMessage}> x </div>
</div>
: null
)
}
}
export default AlertService;
I am geting the following error when trying to call the function setAlert from outside this component.
However if I set the isMessageSet property to true then on clicking the X and calling the closeAlert method, it works fine.
componentDidMount indicates that the component is getting mounted and componentWillUnmount is never getting executed , I am not sure what is wrong here
Error Message
Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the AlertService component.
setState should not be called from outside the component. If you want to change the state from outside, use props.
And as the error message says, the component is not mounted. You could mount it by adding <AlertService /> to the Layout.
As you have mentioned in one of the comments that you are trying to call the setAlert function after instantiating the AlertService class, I suggest you to please take a look as to how you are doing that. The right way is:
this.AlertService = React.render(React.createElement(AlertService), mountnode) //mounts the component
this.AlertService.setAlert() // now you can call the function
Depending upon your usecase you can do like above.
The thing with react is that the child component's methods cannot be called by the parent component. Since those are meant to be private to the child and should therefore be handled by itself. As a hack though we can make use of refs to call the child component's methods but that is not a recommended usecase for refs. Thid can result in bugs in you application.
The best way to achieve the prupose as #vijayst suggested is to change state of your parent component on an alert(whenever message is received) and pass it as a prop to the child. Now update the state for the child under componentWillReceiveProps().
If I understand you correctly you said you tried calling setAlert from another component, it doesn't work but when you called closeMessage it works as expected, but then I noticed you called closeMessage in this same component which would work as expected, if you want to call a function that belongs to this component in another component then you need to import the component into this component then pass the function to it so you would be able to call the function in the component. For example:
import AnotherComponent from '../AnotherComponenet'
<AnotherComponent setAlert={this.setAlert} />

componentWillMount check on undefined before api request will resolve

I have a component that triggers some methods that depend on async API request. I use componentWillmount to check some props. If this prop is true I trigger some function otherwise false. But the problem is, that first time the prop is undefined, only after some time it will become false or true. How to check it and wait until request resolved?
componentWillMount = () => {
this.props.init(parseInt(this.props.tagId, 16), this.props.accessToken);
if (!this.props.notFound && !this.props.person) this.props.onFreeTag();
};
Use componentWillReceiveProps lifecycle function, something like:
componentWillReceiveProps = (nextProps) => {
if (!nextProps.notFound && !nextProps.person)
nextProps.onFreeTag()
}
}
It appears that first time when the component loads or get called your are passing it some value which is undefined initially and later becomes available. For example lets say you have a parent component as following
class Parent extends React.Component {
constructor() {
this.state = {0}
}
render() {
<Child value={this.state.value} />
}
}
As you can see, initially the state doesn't have a property value so the Child will receive undefined for this.props.value. It will only receive not undefined when some parent function changes it like this,
class Parent extends React.Component {
constructor() {
this.state = {0}
}
onAction() {
this.setState({value: true})
}
render() {
<Child value={this.state.value} />
}
}
So if on some event parent calls its OnAction it will change the state and Child will get this.props.value as true but as Child will be already rendered componentWillMount hook will not get triggered but componentWillReceiveProps will. So if you want to use the prop in componentWillMount make sure its passed at the first render of the child and if that is not possible use componentWillReceiveProps to process the props

Reactjs: Uncaught Error: Maximum update depth exceeded when trying to update state

I am trying to do a simple thing:
check if a parameter is on the URL and then set a state for my component. This state will determine if some html code should be displayed or not.
Basically this dummy sample may give you an idea of what I have:
class Lalala extends Component {
constructor(props) {
super(props);
this.state = {showStatement : false}
}
parseURLParams(urlParams) {
if (urlParams.indexOf('blabla')> -1) {
this.setState({
showStatement: true
})
}
}
render() {
const { location } = this.prop
this.parseURLParams(location.search);
}
}
So, as you can see, every time it renders, it calls the parseURLParams function which tries to set the state, and, of course, when the setState is called, the render function is being called again, causing a infinite loop which ends up returning this error in the console.
Could you guys tell me a better way to set this state? once this is something that doesn't depend on an event, I am a bit confused.
Thanks
cause you using setState in render. It willbe render -> setState -> render-> setState -> render... You should move this.parseURLParams(location.search); to other lifecycle like this
componentWillReceiveProps(nextProps) {
if(JSON.stringify(nextProps.location) !== JSON.stringify(this.props.location)){
this.parseURLParams(this.props.location.search);
}
}
Try setting state in a different lifecycle hook, like componentDidUpdate. Also, if the prop value you want is available at the initial render, you'll want to set it in state there as well:
class Lalala extends Component {
constructor(props) {
super(props);
this.state = {
showStatement : false,
showBrokerStatements: props.location && props.location.search && props.location.search.indexOf('blabla')> -1
}
}
componentDidUpdate() {
this.parseURLParams(this.props.location.search);
}
parseURLParams(urlParams) {
if (urlParams.indexOf('blabla')> -1) {
this.setState({
showBrokerStatements: true
})
}
}
render() {
const { location } = this.prop
}
}

Access an object from componentDidMount()

I called the following library in componentDidMount() and it returns an object successfully.
componentDidMount() {
var objData =call.MyLibStart('12121', this);
}
Now I need to use objData in render() section. I also need to access some attribute of the objData such as, objData.
render() {
//I need the object.postate here
}
How can I access an object there? Is using a the state a good idea here?
You can access the object just like #3Dos's answer. If you want to modify the value of objData, then use it as a state. If you only want to render that object or get the value to check for something then a class property is enough.
Make sure you're getting the object the right way:
componentWillMount () {
this.objData = Object.assign({}, call.MyLibStart('12121', this))
console.log('objData: ', this.objData) // inspect the object in debugger to check it's keys
}
The reason with componentDidMount is it only run after the render function. Your app's flow goes like this:
in constructor(): this.objData = null
in render(): this.objData = null
in componentDidMount(): this.objData = some object
At this time render function will not be updated because it will only update if you have made some changes to your state. Since this.objData is not a state so it will always be null in render. So by changing componentDidMount to componentWillMount your objData won't be null when render got called.
What you want is probably to set an instance variable in a constructor so you can access it in your other class methods like for exemple :
class MyComponent extends Component {
constructor (props) {
super(props)
this.objData = call.MyLibStart('12121', this)
}
render () {
// Do whatever you like with this.objData
return null
}
}
Unless you need access to mounted components, you could set it as initial state in the constructor:
constructor(props) {
super(props)
this.state = {
objData: call.MyLibStart('12121', this)
}
}
render() {
// use this.state.objData here
}

Categories

Resources