Passing Props from State to Child - javascript

I am getting data from a WebAPI call in my componentDidMount on my parent react component. I put the values into state.
When I render my form, I am just making custom labels with data (A component), and passing the label text, and the data to this component. (One for each field I am displaying). I pass the values from state, via props, to the child components.
But what I am finding is that my child components are rendering without data being populated... as this seems to happen before the api call happens. The api happens, the state gets set, but data never gets to the child components. I thought that the props would pass the updated state data to the components. I'm wrong. How should I achieve this? I want to load the data in my parent, and then render the child components, passing in the data.
componentDidMount() {
this.loadData();
}
loadData() {
var request = {
method: 'GET',
URL: "http://example.com/api/user/profile",
}
fetchData(request).then(response => {
if(response.errorcode != "OK")
{
console.log("Bad response from API. Need to redirect!")
}
else
{
this.setState(
{
firstname: response.payload.firstname,
surname: response.payload.surname,
email: response.payload.email,
countryId: response.payload.countryId,
countries: response.payload.countries
}
);
}
});
}
render() {
return (
<div>
<h2>Your Profile</h2>
<div className="row">
<div className="col-xs-6">
<DisplayLabel labelText={"Firstname"} data={this.state.firstname} />
<DisplayLabel labelText={"Surname"} data={this.state.surname} />
<DisplayLabel labelText={"Email Address"} data={this.state.email} />
<DisplayLabel labelText={"Country"} data={this.state.countryId} />
<div className="form-group row right">
<button className="btn btn-primary" onClick={this.openModal}>Edit</button>
</div>
</div>
</div>
<Modal isOpen={this.state.modalIsOpen} onAfterOpen={this.afterOpenModal} style={modalStyle}>
<ProfileEditBox closeMeCallback = {this.closeModal} />
</Modal>
</div>
)
}
My display label component is simply this. I'm new to this, and just trying to make reusable components:
import React, {Component} from 'react';
export default class DisplayLabel extends Component {
constructor(props)
{
super(props);
this.state = {
labelText: this.props.labelText,
data: this.props.data
}
console.log(this.state);
}
componentDidMount() {
this.setState({
labelText: this.props.labelText,
data: this.props.data
});
console.log("ComponentDidMount", this.state);
}
render() {
return (
<div>
<div className="row">
<div className="col-xs-12">
<label className="control-label">{this.state.labelText}</label>
</div>
</div>
<div className="row">
<div className="col-xs-12">
<strong><span>{this.state.data}</span></strong>
</div>
</div>
</div>
)
}
}
I need to wait for the API call to complete before I render the form?

This is a common problem in React. I usually try to resolve it with a pattern that shows some sort of loading indicator. So I would initialize your state like this in the constructor:
this.state = {
loading: true
}
And I would change your render to have a check for that loading bool:
render() {
if (this.state.loading) {
return (<h1>Loading, please wait...</h1>);
}
return (
<div>
<h2>Your Profile</h2>
<div className="row">
<div className="col-xs-6">
<DisplayLabel labelText={"Firstname"} data={this.state.firstname} />
<DisplayLabel labelText={"Surname"} data={this.state.surname} />
<DisplayLabel labelText={"Email Address"} data={this.state.email} />
<DisplayLabel labelText={"Country"} data={this.state.countryId} />
<div className="form-group row right">
<button className="btn btn-primary" onClick={this.openModal}>Edit</button>
</div>
</div>
</div>
<Modal isOpen={this.state.modalIsOpen} onAfterOpen={this.afterOpenModal} style={modalStyle}>
<ProfileEditBox closeMeCallback = {this.closeModal} />
</Modal>
</div>
)
}
Then you can set loading to false following a successful data pull, and your form will display correctly without any errors.
(I edited this to use your parent component rather than DisplayLabel, as it makes more sense there. However, the component name is omitted from your question).

I'm not sure you have to wait until the request complete (but you most likely do have to for UI issues), but i guess your problem is something else.
you took the props and included them inside your state (that's unneccery), and then when the component complete it's loading you update the state again, but the request probbaly compleate after the componnent complete it's loading.
if you must to put the props inside your state, you should add componentDidUpdate or componentWillReceiveNewProps lifecycle functions, not the componentDidMount.
but, if you don't have to (most likley), you might change your render funcion to:
render() {
return (
<div>
<div className="row">
<div className="col-xs-12">
<label className="control-label">{this.props.labelText}</label>
</div>
</div>
<div className="row">
<div className="col-xs-12">
<strong><span>{this.props.data}</span></strong>
</div>
</div>
</div>
)
}
(just changed the state to props)
Try that, Good Luck!

Just because constructor called once when component mount so you can do one thing, in your render take two var and assign data into that. And then use those variable to show data. Because child component will render Everytime state of parent will change but constructor will not.

Related

ReactJS: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state

Hello iam newbie in ReactJS, I'm using a library react-bootstrap4-form-validation to validate my form. From the documentation there are the function that use to reset the form validation. From the documentations there are example to reset the form validation using triggering button onClick, like this. And for a condition, i need to reset the validation, but not when triggering button onClick, but when the props from parent component is change. Because of these needs I also created a function (useCompare) that is used to compare the props received from the parent component.
In short, I want to reset my form, when the props received from the parent component changes, and the code is as shown below.
import React, { useRef } from "react";
import { ValidationForm, TextInput } from 'react-bootstrap4-form-validation';
import { useCompare } from '../../services/compare-props';
function TestForm({ form }) {
const formRefs = useRef();
const handleSubmit = (e, formData, inputs) => {
e.preventDefault();
console.log(formData)
}
if ( !useCompare(form) ) {
resetForm()
}
function resetForm() {
let formRef = formRefs.current;
formRef != null ? formRef.resetValidationState(true) : null;
}
return (
<div className="row justify-content-center">
<div className="col-md-6 col-sm-10">
<div className="shadow-sm p-3 mb-5 bg-white rounded border">
<h6>{form.labelForm}</h6>
<hr />
<ValidationForm onSubmit={handleSubmit} id="form-test" ref={formRefs}>
{form.field.map(function (fields, index) {
return (
<div className="row form-group mb-1" key={index}>
<div className="col-lg-4 col-sm-4 col-md-4">{fields.label}</div>
<div className="col-lg-8 col-sm-8 col-md-8">
<TextInput
type={fields.type}
className="form-control"
name={fields.name}
autoComplete="off"
required
{...(form.formType == 'add' && fields.name == 'brand_code' ? {minLength : "4"} : {})}
/>
</div>
</div>
);
})}
<button type="submit" className="btn btn-danger">
Save
</button>
<button type="button" className="btn btn-warning" onClick={() => resetForm()}>
Reset Form
</button>
</ValidationForm>
</div>
</div>
</div>
);
}
export default TestForm;
The code above works fine when the props received from the parent component doesn't change, and when I try to reset the form via onClick trigger button, it works fine too. But when I want to reset the form when the props of the parent component changes, it generates an error like this:
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state
Can anyone help me solve this problem? I am very grateful beforehand.
Try moving useCompare check to a side effect:
useEffect( () => {
if ( !useCompare(form) ) {
resetForm()
}
}, [useCompare, form, resetForm] )
You will likely have to wrap resetForm in a useCallback hook.
This useEffect will run every time form changes, and placing it here should prevent the 'update during render' issue.

Load the component when all data has ready

I'm using React with Redux.
In this example I have my class with mapStateToProps and mapDispatchToProps
class EnigmaPage extends Component {
constructor(props){
super(props);
}
componentDidMount() {
this.props.authCheckState();
}
readUserData() {
this.props.loadLevel(this.props.userId);
}
render(){
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
</div>
</div>
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
authCheckState: () => dispatch(actions.authCheckState()),
getLevel: () => dispatch(actions.getLevel()),
loadLevel:(id) => dispatch(actions.loadLevel(id))
};
}
const mapStateToProps = state => {
return {
userId:state.auth.user,
level:state.level.level
}
}
I wanna push to my component LevelInfo the values difficulty and level but these 2 data arrive from getLevel() that is an http request with delay.
The page loads before receiving all the data from the http call.
I'm looking a way to wait to load the component LevelInfo or reload the component when the data are all ready.
You need to tell your component that will wait for the data needed to render your Level component, so into your EnigmaPage component write as follow
render(){
const { level } = this.props;
if (!level) { return <LoadingComponentSpinner />; } // this will render only when level attr is not set, otherwise will render your `LevelInfo` component
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
</div>
</div>
</div>
)
}
I hope it can help you.
We don't make our components wait, we let the initial rendering happens but render the target component with a conditional expression.
render() {
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
{this.props.level && (
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
)}
</div>
</div>
</div>
);
}
So, here we are checking if this.props.level is defined. When it is undefined React does not render anything, after getting the data LevelInfo component is rendered. You can change conditional rendering logic. You can render a Loading component maybe instead of nothing. But, at the end of the day, you will render your component conditionally.
try to use conditional rendering:
isDataReady() {
if(this.props.level.difficulty && this.props.level.level)
return true;
return false;
}
render(){
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
{this.isDataReady() ? <LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
: <div> </div>}
</div>
</div>
</div>
);
}
in case your data is not ready you can display anything you want, for simplicity here I just added an empty <div>.
Johuder Gonzalez has talked about the Spinner. The Material UI has a lot of examples, which is easy to apply. Please look the followings.
Material UI Progress

React doesn't re-render on props change

I am kinda new to react and to the webitself.
this is my render function
render() {
const {repositories} = this.props
return (
<div className='mt4 bt b--black-20 boardingbox scrollarea-content' style={{overflow: 'scroll', height: '100vh'}}>
{
repositories.map((repo, index) => {
console.log(repo.name)
return <Note name={repo.name} desc={repo.name} key={index} onClick={ this.handleClick.bind(this) }/>
})
}
</div>
)
}
The repositories is changing the way I want, but for some reason the its not get re-rendered. I passing the repositiores property from the parent.
The first time I render it (click to the search button, get a response from the server, and set the repo array), its working fine. But at the 2nd search, when there is something in the array, its not working properly, and not re-render.
UPDATE:
The parent's render / onClick
render() {
const {repositories} = this.state
return (
<div className='w-third navpanel br b--black-20'>
<SearchBar onClick={this.onClick} onChange={this.onChange}/>
<RepoList repositories={repositories}/>
</div>
//<NewNote />
//<Tags />
//<NoteList />
);
}
onClick = (event) => {
const {searchTerm} = this.state
let endpoint = 'https://api.github.com/search/repositories?sort=stars&order=desc&q=' + searchTerm;
fetch(endpoint)
.then(blob => blob.json())
.then(response => {
if(response.items)
this.setState({ repositories: response.items });
})
}
UP-UPDATE:
Search Comp:
constructor({onClick, onChange}) {
super()
this.onClick = onClick
this.onChange = onChange
this.state = {
imageHover: false
}}
render() {
return (
<div className='flex items-center justify-between bb b--black-20'>
<div className='ma2 inputContainer w-100'>
<input className='pa1 pl4 boardingbox w-100 input-reset ba b--black-20 br4 black-50 f6' placeholder='repos' type="text" onChange={this.onChange}/>
</div>
<div className='mr2'>
<div className='boardingbox pointer contain grow'>
<img src={(this.state.imageHover) ? NoteImageOnHover : NoteImage} alt=''
onMouseOver={()=>this.setState({imageHover: true})}
onMouseOut={()=>this.setState({imageHover: false})}
onClick={this.onClick}/>
</div>
</div>
</div>
)}
first responde
second responde
and I am really ashamed that I could screw up like this.
So basicly the problem was:
return <Note name={repo.name} desc={repo.name} key={index} onClick={ this.handleClick.bind(this) }/>
So I was as stupid to use INDEX as a KEY so I could not add again the same key to the array.
Thanks anyway guys! :)
The root cause most probably is due to error in function binding.
In your SearchComponent you are using the "props" to create function bindings in the contructor. This can cause your SearchComponent to refer to wrong instance of the functions for onClick and onChange. Would suggest referring to the official documentation for more details.
you do not need to rebind the functions in your SearchComponent, you can just use the functions received in props.
<input className='pa1 pl4 boardingbox w-100 input-reset ba b--black-20 br4 black-50 f6' placeholder='repos' type="text" onChange={this.props.onChange}/>
<!-- snipped other irrelevant code -->
<img src={(this.state.imageHover) ? NoteImageOnHover : NoteImage} alt=''
onMouseOver={()=>this.setState({imageHover: true})}
onMouseOut={()=>this.setState({imageHover: false})}
onClick={this.props.onClick}/>
Why could be happening to cause this behavior
Remember, constructor is only called once the component instance is being constructed, once it has been created and remains alive, React lifecycles take over.
So, when you first render your screen, the component is created and since there is only 1 of everything, it kind of works.
When you run your first search: onChange/onClick callbacks modify the state of the parent component. Which then calls render on the parent component.
At this point, it is possible that your SearchComponent maybe holding on to the wrong instance of the call back methods, which would thus not set state on the parent and thus not force re-render.
Additional Notes on your constructor
Normally you shouldn't refer to props in your constructor, but if you need to, then you need to have it in the format below. Here are the relevant docs:
constructor(props) {
super(props);
// other logic
}

Update functionality in react

I am newbie to React and I am trying to do update on react. I don't get the exact logic to make it and hence I need your help.
On click of update, I managed to get the values of selected contact but later on, i don't get how to populate those value onto input text boxes and again on submit after change of values, update the selected contact. I came across onChange but I don't understand.
Clues i knew:
this.refs.name.value and this.refs.number.value are values which are in input textbox . And on update, we need to set these value into the state on that corresponding index.
My code and screenshot is below:
Person.js - number is taken as the key , considering individual number is unique
editcontact(id){
this.props.onChange(id);
}
render() {
return(
<div className="panel panel-default">
<div className="panel-heading">
<h4>{this.props.detail.name} </h4>
<a className="b" href="#" onClick={this.deletecontact.bind(this,this.props.detail.number)}> Delete </a>
<a className="b" href="#" onClick={this.editcontact.bind(this,this.props.detail.number)}> Update </a>
</div>
<h6 className="panel-body">{this.props.detail.number}</h6>
</div>
</div>
)
}
It is passed to Contact.js
editcontact(id)
{
this.props.onChange(id);
}
render() {
var details;
if(this.props.data){
details=this.props.data.map(dts=>{
return(
<Person key={dts.number} detail={dts} onChange={this.editcontact.bind(this)} onDelete={this.deletecontact.bind(this)}></Person>
)
})
}
Then comes App.js
handleEdit(id){
console.log(id);
let cts=this.state.contacts;
let index=cts.findIndex( x => x.number === id);
console.log(cts[index]);
this.setState({ selectedContact: cts[index]; });
}
render() {
return (
<div className="App">
<div className="page-header">
<h2>Contact list</h2>
</div>
<AddContact newOne={this.state.selectedContact} addcontact={this.handleAddition.bind(this)}></AddContact>
<Contact onChange={this.handleEdit.bind(this)} onDelete={this.handleDelete.bind(this)} data={this.state.contacts}> </Contact>
</div>
);
}
AddContact.js
constructor(){
super();
this.state={
newContact:{
name:'',
number:''
}
}
}
addcontact(e){
// console.log(this.refs.name.value);\
e.preventDefault();
this.setState({
newContact:{
name: this.refs.name.value,
number:this.refs.number.value
}
},function(){
console.log(this.state.newContact);
this.props.addcontact(this.state.newContact);
})
this.refs.name.value="";
this.refs.number.value="";
}
render() {
console.log(this.props.newOne);
return (
<div className="col-md-6">
<form onSubmit={this.addcontact.bind(this)}>
<div className="form-group">
<label>Name </label>
<input className="form-control" type="text" ref="name" />
</div>
<div className="form-group">
<label>Number</label>
<input className="form-control" type="number" ref="number" />
</div>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
what you need is to tell your component that you have a new state and you want it to re-render.
handleEdit(id){
console.log(id);
let cts=this.state.contacts;
let index=cts.findIndex( x => x.number === id);
this.setState({ selectedContact: cts[index]; });
}
render() {
return (
<div className="App">
<div className="page-header">
<h2>Contact list</h2>
</div>
<AddContact addcontact={this.handleAddition.bind(this)}></AddContact>
<Contact onChange={this.handleEdit.bind(this)} onDelete={this.handleDelete.bind(this)} data={this.state.contacts}> </Contact>
</div>
);
with the setState function you updating the state of this compoent and also make it to re-render. now you can decide what you want to do with this data: this.state.selectedContact like passing it to AddContact
Don't use .bind(this... there is no reason to do it. Use attribute={() => this.functionName()}
Don't use different naming, use some pattern for attributes names. e.g. addcontact should be addContact
Don't use so long lines. Use Eslint to show you all of such tips.
It's really hard to read your code now, make it more readable and you will have better time editing it yourself.
And now to have update, i would suggest using pure functional component to display things and higher order component to manage state of data.
(props) => <form> .... <input value={props.email} /> ... </form;
and in parent component, which is responsible for all data management add state. In state you can save values and pass it into child components using props.
When you will advance in React, you will start using extra libraries to manage state of the app, e.g. Redux. It makes similar thing, but all app's state is in one place and then you can access it from any part of the app. E.g. you show these inputs, then jump to another state of app to add some other thing and then you can easily jump back to this state and still have input's values that are partly entered.
Just save values in state. No matter how you manage your app's state and push values to display components using props. Google, read, check some videos on Youtube and you will get it.
https://facebook.github.io/react/docs/thinking-in-react.html

How to force remounting on React components?

Lets say I have a view component that has a conditional render:
render(){
if (this.state.employed) {
return (
<div>
<MyInput ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div>
<MyInput ref="unemployment-reason" name="unemployment-reason" />
<MyInput ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}
MyInput looks something like this:
class MyInput extends React.Component {
...
render(){
return (
<div>
<input name={this.props.name}
ref="input"
type="text"
value={this.props.value || null}
onBlur={this.handleBlur.bind(this)}
onChange={this.handleTyping.bind(this)} />
</div>
);
}
}
Lets say employed is true. Whenever I switch it to false and the other view renders, only unemployment-duration is re-initialized. Also unemployment-reason gets prefilled with the value from job-title (if a value was given before the condition changed).
If I change the markup in the second rendering routine to something like this:
render(){
if (this.state.employed) {
return (
<div>
<MyInput ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div>
<span>Diff me!</span>
<MyInput ref="unemployment-reason" name="unemployment-reason" />
<MyInput ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}
It seems like everything works fine. Looks like React just fails to diff 'job-title' and 'unemployment-reason'.
Please tell me what I'm doing wrong...
Change the key of the component.
<Component key="1" />
<Component key="2" />
Component will be unmounted and a new instance of Component will be mounted since the key has changed.
Documented on You Probably Don't Need Derived State:
When a key changes, React will create a new component instance rather than update the current one. Keys are usually used for dynamic lists but are also useful here.
What's probably happening is that React thinks that only one MyInput (unemployment-duration) is added between the renders. As such, the job-title never gets replaced with the unemployment-reason, which is also why the predefined values are swapped.
When React does the diff, it will determine which components are new and which are old based on their key property. If no such key is provided in the code, it will generate its own.
The reason why the last code snippet you provide works is because React essentially needs to change the hierarchy of all elements under the parent div and I believe that would trigger a re-render of all children (which is why it works). Had you added the span to the bottom instead of the top, the hierarchy of the preceding elements wouldn't change, and those element's wouldn't re-render (and the problem would persist).
Here's what the official React documentation says:
The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a key.
When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).
You should be able to fix this by providing a unique key element yourself to either the parent div or to all MyInput elements.
For example:
render(){
if (this.state.employed) {
return (
<div key="employed">
<MyInput ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div key="notEmployed">
<MyInput ref="unemployment-reason" name="unemployment-reason" />
<MyInput ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}
OR
render(){
if (this.state.employed) {
return (
<div>
<MyInput key="title" ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div>
<MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
<MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}
Now, when React does the diff, it will see that the divs are different and will re-render it including all of its' children (1st example). In the 2nd example, the diff will be a success on job-title and unemployment-reason since they now have different keys.
You can of course use any keys you want, as long as they are unique.
Update August 2017
For a better insight into how keys work in React, I strongly recommend reading my answer to Understanding unique keys in React.js.
Update November 2017
This update should've been posted a while ago, but using string literals in ref is now deprecated. For example ref="job-title" should now instead be ref={(el) => this.jobTitleRef = el} (for example). See my answer to Deprecation warning using this.refs for more info.
Use setState in your view to change employed property of state. This is example of React render engine.
someFunctionWhichChangeParamEmployed(isEmployed) {
this.setState({
employed: isEmployed
});
}
getInitialState() {
return {
employed: true
}
},
render(){
if (this.state.employed) {
return (
<div>
<MyInput ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div>
<span>Diff me!</span>
<MyInput ref="unemployment-reason" name="unemployment-reason" />
<MyInput ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}
I'm working on Crud for my app. This is how I did it Got Reactstrap as my dependency.
import React, { useState, setState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import firebase from 'firebase';
// import { LifeCrud } from '../CRUD/Crud';
import { Row, Card, Col, Button } from 'reactstrap';
import InsuranceActionInput from '../CRUD/InsuranceActionInput';
const LifeActionCreate = () => {
let [newLifeActionLabel, setNewLifeActionLabel] = React.useState();
const onCreate = e => {
const db = firebase.firestore();
db.collection('actions').add({
label: newLifeActionLabel
});
alert('New Life Insurance Added');
setNewLifeActionLabel('');
};
return (
<Card style={{ padding: '15px' }}>
<form onSubmit={onCreate}>
<label>Name</label>
<input
value={newLifeActionLabel}
onChange={e => {
setNewLifeActionLabel(e.target.value);
}}
placeholder={'Name'}
/>
<Button onClick={onCreate}>Create</Button>
</form>
</Card>
);
};
Some React Hooks in there

Categories

Resources