React - Check If Props Exist - javascript

I searched around the internet for an answer to this question, and I didn't find one. Therefore I am posting my question here.
I have a parent component (App) and a child component (Child).
The App component has a state with some data in it, like so:
class App extends Component {
constructor() {
super()
this.state = {
currentOrganization: {
name: 'name',
email: 'email'
}
}
render() {
return (
<Child data={this.state.currentOrganization} />
)
}
}
}
In my Child component, I have a form:
class Child extends Component {
constructor() {
super()
this.state = {
formData: {
name: '',
email: '',
}
}
}
render() {
return (
<Form ... />
)
}
}
According to the React docs, forms generally should have a state containing properties that correspond with each element of the form. The form that lies in my Child component must have the data of the currentOrganization (as seen in the App component) pre-populate into itself.
In order to accomplish this, I have to set the state of the Child to the props it receives from its parent.
What's the best way to check if my Child component received the props it needs in order to update its own state?

You can assign default props to component.
class Child extends Component {
constructor(props) {
super(props);
this.state = {
formData: {
name: props.name,
email: props.email,
}
}
}
render() {
return (
<Form ... />
)
}
}
Child.defaultProps = {
name: '',
email: '',
};
P.S.
props is JS object so You can check property like this
"prop_name" in this.props // true|false

Related

problem with getDerivedStateFromProps when child component make setstate in parent

I have 3 components. Grandparent, Parent and Child.
if a data change in Grandparent I pass it to Parent as a prop so I can trigger change in Parent and set new data to its state using getDerivedStateFromProps.
export class Parent extends Component {
constructor(props) {
super(props);
this.state = {
userId : this.props.userId
}
}
static getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.userId !== prevState.userId)
return {
userId : this.props.userId
}
return null
}
getDataFromChild(value){
this.setState({
userId: value
})
}
render(){
return (
<Child onChange={(value) => this.getDataFromChild(value)} />
)
}
}
there is also a prop in Child component called onChange. When a data change in Child Component I use this.props.onChange(data) to passing it to parent.
so I want to store this data in Parent's state.
export class Child extends Component {
constructor(props) {
super(props);
this.state = {
userId: this.props.userId
}
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.userId !== prevState.userId)
return {
userId: this.props.userId
}
return null
}
async passToParent(value) {
await this.setState({
userId: value
})
this.props.onChange(value)
}
render() {
return (
<input type="text" onChange={(e) => this.passToParent(e.target.value)} value={this.state.userId}/>
)
}
}
the problem is when any states or props change in Parent component, getDerivedStateFromProps is triggered and because nextprops.userId is not equal to prevState(new state that has been set by Child), the old userId(comes from Grandparent) set to state.
what should I do.
sorry for my bad english skill.
If you would like the state in Parent to change when a value in Grandparent changes, then the quickest way forward would be to use componentDidUpdate:
export class Parent extends Component {
constructor(props) {
super(props);
this.state = {
userId : this.props.userId
}
}
getDataFromChild(value){
this.setState({
userId: value
})
}
componentDidUpdate(prevProps) {
if(prevProps.userId !== this.props.userId) {
this.setState({
userId: this.props.userId,
});
}
}
render(){
return (
<Child onChange={(value) => this.getDataFromChild(value)} />
)
}
}
If, on the other hand, if you don't want state in Parent to change once the user has set it to something, then you can manually set a state that indicates that Child has set it and use it in componentDidUpdate.
getDataFromChild(value){
this.setState({
userId: value,
setByChild: true,
})
}
componentDidUpdate(prevProps) {
if(!this.state.setByChild && prevProps.userId !== this.props.userId) {
this.setState({
userId: this.props.userId,
});
}
}
Related:
You don't need derived state - React Blog

react: reach the component's parent or transfer data from parent to child

Please tell me how you can in this scheme
render()
{
return (
<Parent>
<Child1>
<Child2>
</Parent>
);
}
on the side of the components Parent and Child get the data that will be generated in the render () method or in the constructor (data access)
It is clear that one of the ways is to transfer to props, but if without it?
For example, a state is set, in the Parent constructor, this state of the parent is obtained and the same state is set, in the Child constructor, the Parent state is obtained and its state is set. As a result, some parameter is passed across the tree.
But in order to implement this, you need to somehow gain access to the parent of the component.
For example:
more beautiful option (and I do not know how to implement it - this option would be more preferable)
this.state = {
mydata: 123;
};
class Parent extends Component
{
constructor()
{
super();
this.state = {
mydata: this.getOwner().state.mydata; // = 123
};
}
}
class Child extends Component
{
constructor()
{
super();
this.state = {
mydata: this.getOwner().state.mydata; // = 123
};
}
}
less beautiful option (I know how to implement it)
render()
{
return (
<Parent mydata = "123">
<Child1 mydata = "123">
<Child2 mydata = "123">
</Parent>
);
}
class Parent extends Component
{
constructor(props)
{
super();
this.state = {
mydata: props.mydata
};
}
}
class Child extends Component
{
constructor(props)
{
super();
this.state = {
mydata: props.mydata
};
}
}
you can achieve the wanted result by using something called Context as the following:
//outside of your class component
const MyContext = React.CreateContext();
//inside your class component
state = {mydata: "123"}
render()
{
return (
<MyContext value={this.state}>
<Parent>
<Child1>
<Child2>
</Parent>
</MyContext>
);
}
now inside any of the tree components: Parent Child1 and Child2 you can reach the value of the shared state by saying this.context.mydata
learn more at https://reactjs.org/docs/context.html

Passing child props to child component in Gatsby V2

Since upgrading to using Gatsby V2 I have been struggling to pass this.state to child components to be used as this.props.
For Example I have a Container that has data1 and data2 added to this.state as
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
data1: '',
data2: ''
};
}
componentDidMount() {
// Loading database
.then(doc =>
this.setState({
data1: doc.data().data1,
data2: doc.data().data2
})
);
}
render() {
const children = this.props;
const stateAsProps = React.Children.map(children, child =>
React.cloneElement(child, {
data1: this.state.data1,
data2: this.state.data2
})
);
return (
<div>{stateAsProps}</div>
);
}
}
and a child component as
class Child extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<h1>{this.props.data1}</h1>
<p>{this.props.data2}</p>
);
}
}
and finally this is brought into the page by
const Page = () => (
<Parent authUserID="01234" campaignID="56789">
<Child />
</Parent>
);
In Gatsby V1 this was working but now with the migration I am receiving an error Uncaught Error: Objects are not valid as a React child (found: object with keys {authUserID, campaignID, children}). If you meant to render a collection of children, use an array instead.
Can anyone advise to why and how this issue can be rectified?
You are using the entire props object as children in your Parent component. Make sure you destructure out the children object from the props instead and it will work as expected.
const { children } = this.props;

How to set props on dynamic created child

Within JSX syntax i can easily set a child prop to follow parent's state, like:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { attr1: 10 }
}
render() {
return (
<div>
<Child prop1={this.state.attr1} />
</div>
)
}
}
So whenever state.attr1 changes, Children will have it prop1 changed.
How can i get the same behavior with dynamic created childs:
class Parent extends React.Component {
constructor(props) {
super(props);
this.buildChildren = (items) => {
items.map(item =>
React.createElement(
childrenListWithClasses[item['childrenClass']],
{ key: item['id'], data: {} }
)
)
}
this.state = {
children: buildChildren(this.props.childrenList)
}
}
componentWillReceiveProps(nextProps) {
// maybe here i want to do something like
nextProps.data.forEach(item =>
ReactDOM.findDOMNode(item['id']).setProps({ data: item['data']}) )
}
render() {
return (
<div>
<div>{this.state.children}</div>
</div>
)
}
}
This piece of code does not work, also i've read that setProps is deprecated. Another approach would be create the child with props ~binded to this.state.data if data data is structured as obj:
React.createElement(
childrenListWithClasses[item['childrenClass']],
{ key: item['id'], data: this.state.data[item['id']] }
)
But seems like it tries to evaluate this.state.data[item['id']] at execution.
I've found a suggestion to use cloneWithProps, but this doesn't seem right... If JSX can change child's props why cant i do it with code? also it would not make if i want my child elements to smoothly transition when changing props, child's lifecycle would be ignored.

What does "Warning: setState(...): Can only update a mounted or mounting component" mean?

So I have one root component and two child components. I have trying to get one child to call a method that is up in in the root component and update the state up in the root component, and pass the updated down to the other component, but I am getting the following error.
What could be the issue?
warning.js?8a56:36 Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the firstChild component.
Here is the code:
firstChild.js
export default class firstChild extends React.Component {
constructor(props) {
super(props);
this.state = {
nameText: '',
}
}
nameChange(event) {
this.setState({
nameText: event.target.value,
})
}
submitClick() {
var nameText = this.state.nameText;
this.props.saveName(nameText)
this.setState({nameText: ''});
}
render() {
var st = this.state;
var pr = this.props;
return (
<input
placeholder='Enter Name'
onChange={this.nameChange.bind(this)}
value={this.state.nameText}
/>
<button
onClick={this.submitClick.bind(this)}
/>
And in root component, App.js:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
submitSuccess: false
}
}
saveName(nameText) {
this.setState({submitSuccess: true});
}
render() {
var props = {};
props.submitSuccess = this.state.submitSuccess;
return (
<div>
<firstChild
saveName={this.saveName.bind(this)}
/>
{React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, props);
})}
</div>
)
}
}
And my secondChild.js:
export default class secondChild extends React.Component {
static propTypes = {
submitSuccess: React.PropTypes.bool.isRequired,
}
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div>
{this.props.submitSuccess}
</div>
)
}
}
Fisrt, rename all your React components as Camel Case like this.
class firstChild ... --> class FristChild
<fristChild> --> <FristChild>
Second, in your FirstChild render method, you should wrap your elements into an enclosing tag like this:
class FirstChild extends Component {
render(){
return (
<div>
<input ... />
<button ... />
</div>
)
}
}
Third, when you use cloneElement upon this.props.children, you should use Proptypes.<type> in your secondChildren instead of Propstypes.<type>.isRequired. Check it here to see why.
class SecondChild extends Component {
static propTypes = {
submitSuccess: React.PropTypes.bool, // remove isRequired
}
}
Regardless all above, I have tested your code and it works fine.
You can try and use componentWillUnmount lifecycle function in order to check when the component is unmounted.
You can also use a flag to signal that the component is unmounted before setting the state:
saveName(nameText) {
if (!this.isUnmounted){
this.setState({submitSuccess: true});
}
}
componentWillUnmount() {
this.isUnmounted = true;
}

Categories