Why child component didnt get parent's props with Axios in React - javascript

I solve it with a simple flag like so:
I added a new flag property on the states object: loaded:false
I update the loaded value to true, when i get the data, like so:
helpers.getValues().then(results => this.setState({values:results.data,loaded:true}));
And finally, inside the render() i first check if the loaded==true and then i render the ChildComponent, like so:
{this.state.loaded == true ? <ChildComponent values={this.state.values} name="theodore"/> : ''}
I am making a simple code in React that gets data with Axios.
The data are returned to the client , so the communication is OK.
The problem is that I pass the results data to a child component , as props, and as soon as the child component loads it asks for the props.data but it is empty.
Below is the Parent component:
Inside the componentDidMount I call the axios to get the data and update the setState object.
Below, into the render function, I pass the results to the ChildComponent.
var ParentComponent = React.createClass({
getInitialState:function(){
return {
values:''
activeTab:0
}
},
componentDidMount:function(){
helpers.getValues().then(results => this.setState({values:results.data}));
},
render:function(){
return(
<div className='container'>
<div className="row">
<div className="col-sm-8 text-center" >
<h1>{pageTitle}</h1>
<Tabs activeKey={this.state.activeTab} onSelect={this.handleSelect} id="controlled-tab-example">
<Tab eventKey={tabs[0].key} title={tabs[0].name}>
Tab1
<ChildComponent1 values={this.state.values} test="ok"/>
</Tab>
<Tab eventKey={tabs[1].key} title={tabs[1].name}>Tab2</Tab>
</Tabs>
</div>
</div>
</div>
)
}
And here i show the ChildComponent(in a seperate js file).
Inside the componentDidMount I try to show the props but the object that gets it like:
{values:'',test:'ok'}
var ChildComponent = React.createClass({
componentDidMount:function(){
console.log(this.props);
},
render:function(){
return(
<div>
<ul>'nothing'</ul>
</div>
)
}
});
I guess that it is a delay issue, in which the chld component loads before the axios async returns the data from server
Any help from someone that has dealt with a similar situation would be appriciated, thanks.

Because you're passing this.state.movies, instead of this.state.values to the ChildComponent.
How you do it:
<ChildComponent1 values={this.state.movies} test="ok"/>
How it should be:
<ChildComponent1 values={this.state.values} test="ok"/>

Related

prevent re render component using React and React-memo

I would like to prevent component re-rendering using React. I've read some guides but I'm still having trouble getting my code to work.
The CreateItem component creates an input form from the json object. When the input states change, React re-renders all components. I would avoid this situation as it causes some problems.
I have used React.memo but my code still doesn't work. Is this a good way to implement this code? How can I correct my code? Thank you
function MyComponent() {
return(
<div className="row">
{Array.from(new Map(Object.entries(json))).map((data) => (
<CreateItem obj={data} />
))}
</div>
);
}
//function CreateDiv(props) {
const CreateDiv = React.memo((props) => {
console.log("rendering ");
return (
<form name="myForm" onSubmit= {formSubmit}>
<div className="row">
{Array.from(new Map(Object.entries(props.obj[1]))).map((data) => (
<>
{(() => {
return(
<div className="col-sm-2">
<CreateItem obj={data[1]} />
</div>
)
})()}
</>
))}
</div>
</form>
);
});
--- EDIT ---
CreateItem uses CreateCheckBoxComponent function to create my custom checkbox with default status from json value.
CreateCheckBoxComponent code is follwing:
function CreateCheckBoxComponent(props) {
if(parseInt(props.obj.defaultValue) === 5)
setChecked(false);
else
setChecked(true);
return(
<FormCheck
label={props.obj.simbolName}
name={props.obj.idVar}
type="checkbox"
checked={checked}
onChange={handleCheckBoxChange}
sm={10}
/>
);
}
HandleCheckBoxChange works fine and changes state, but when I click on checkbox to change the flag, CreateCheckBoxComponent is re-render and
it sets the default state again. I would like to avoid this problem and I think preventing re-rendering can be a solution..
React.memo only prevents own rerendering.
You have considered the following things.
If the children are using React.memo but the parent re-renders
the children will render also.
React.memo prevents re-rendering if the component's state changes. but if the prop changes, the component re-renders.
Note: make sure when you render elements/Components with the map function or any iteration always provide a unique key to them.
For more information click here

How to import a component that is rendered in another component?

I have a form component rendered in a component, component1, and I want to use it in another component, component2, but I want to use it with all the functionality that it has inside component1. How can I do this? I tried exporting it from inside component1 but it didn't work.
Here is where the form component is rendered in component1:
return (
<OutsideClickHandler onOutsideClick={this.handleBlur}>
//some not important code was here
<div
id={id}
className={popupClasses}
ref={node => {
this.filterContent = node;
}}
style={contentStyle}
>
{this.state.isOpen ? (
<FilterForm // this FilterFrom I want to export into another component
id={`${id}.form`}
paddingClasses={popupSizeClasses}
showAsPopup
contentPlacementOffset={contentPlacementOffset}
initialValues={initialValues}
keepDirtyOnReinitialize={keepDirtyOnReinitialize}
onSubmit={this.handleSubmit}
onChange={this.handleChange}
onCancel={this.handleCancel}
onClear={this.handleClear}
>
{children}
</FilterForm>
) : null}
</div>
</div>
</OutsideClickHandler>
);
}
}
Here is where I want to import and use the form component:
return (
<div className={classes}>
//some not important code was here
<ModalInMobile
id="SearchFiltersMobile.filters"
isModalOpenOnMobile={this.state.isFiltersOpenOnMobile}
onClose={this.cancelFilters}
showAsModalMaxWidth={showAsModalMaxWidth}
onManageDisableScrolling={onManageDisableScrolling}
containerClassName={css.modalContainer}
closeButtonMessage={modalCloseButtonMessage}
>
//here I want to import and use the *form component*
</ModalInMobile>
</div>
);
FilterForm job is to perform certain logics and display some elements in the DOM, based on its props and states (Just like how system works, you can have a car parked or have a car running both are the same system but with different inputs and states). So you already have your component. If you mean that attributes like popupSizeClasses, contentPlacementOffset, keepDirtyOnReinitialize, .... are shared between component1 and component2, you can make a wrapper around FilterForm with those values passed to FilterForm. So you would render FilterFormWrapper component instead.
But make sure that some props like onCancel (which is equal to this.handleCancel) are independent and does not have any dependency (state or props) to component1, so you can extract them.

Trouble iterating/mapping props

I'm trying to develop a simple application in which any number of tasks will be rendered as cards. I'm passing them as props, schemed like so:
taskList: [{
taskID: 1,
taskTitle: 'Task 1',
taskDescription: 'Description 1',
completed: true
}]
By logging the props in the TaskCard component, I can see the list arrives exactly like that. If I try and log something such as props[0].taskDescription, it'll successfully return "Description 1", like so:
export default function TaskCard(props) {
return(
<div className="task-card" draggable>
<h3> Test </h3>
{ props[0].taskDescription } // this actually works
</div>
)
}
I can't, however, map or calculate the length of the props to iterate through props.
What am I doing wrong in terms of iteration? Is it a flawed architecture in terms of componentization?
To render a list of TaskCards, you need to do the mapping of taskList outside that component like so:
{taskList.map(task => <TaskCard task={task} />)}
and then TaskCard would render using the passed task props:
TaskCard(props) {
// use props.task.taskDescription, etc.
}
First, thank you all who contributed with your insights in the comments for the original posting. So yes, apparently passing an array as a prop is troublesome. I may not have resolved in the most efficient way, but here's what it has come down to so far. Use the image in the original post to guide yourself.
I have resolved the issue the following way:
UserArea.jsx
Contains a state that logs the user, username, a list containing pending tasks pendingList and a list containing complete tasks completedList;
It renders a header, the PendingArea and CompleteArea component passing {...this.state} to each of them (the full component is an object).
render(){
return(
<div className="user-area-container">
<div className="profile-header">
<h2>{ this.state.userName }</h2>
{ this.state.email }
</div>
<PendingArea {...this.state} />
<CompletedArea {...this.state} />
</div>
)
}
PendingArea.jsx and CompleteArea.jsx
Here I made the filtering, passing only the equivalent mapping to each of the components. The code for the PendingArea component is as follows:
function PendingArea(props) {
var pendingTasks = props.pendingList.map(task => {
return <TaskCard {...task} />
});
return(
<div className="status-container">
<div className="status-title">
<h2>Pending tasks</h2>
</div>
<div className="status-modifier">
{ pendingTasks }
</div>
</div>
)
}
TaskCards.jsx
Finally, the TaskCard component uses the direct properties from the props:
export default function TaskCard(props) {
return(
<div className="task-card" draggable>
<h3> { props.taskTitle } </h3>
{ props.taskDescription }
</div>
)
}
This way I managed to properly render the task card in their due place. I hope this works for anyone reading this, as well.

Update data in parent when added in child in react

I'm new to React. I see a lot of posts for updating children when the parent is updated but I haven't found the opposite.
I have a parent component (MainComponent) fetching and storing data. The form to add data (FormNewSubItem ) is in a child component.
When I submit the form I add a subitem in an item and I want the array of items in main component to store the new data.
In MainComponent.jsx
storeItems(items) {
// store items in db
}
retrieveItems() {
// return items from db
}
render {
return (
<MainComponent>
<ListItems items={this.retrieveItems()} />
</MainComponent>
)
}
In ListItems.jsx
render {
return (
<div>
{this.props.items.map((item, idx) => <Item item={item} key={idx} />)}
</div>
)
}
In Item.jsx
handleNewSubItem = (subItem) => {
this.props.item.addSubItem(subItem);
// how to update the 'fetchedItems' in MainComponent ?
}
render {
return (
<React.Fragment>
<div className="title">{this.props.item.title}</div>
<div className="content">
<ListSubItems subitems={this.props.item.subitems}>
<FormNewSubItem handleNewSubItem={thid.handleNewSubItem} />
</ListSubItems>
</div>
</React.Fragment>
)
}
So there are a few ways you can do this. A common way would to connect both components to the same set of data using a library like Redux.
But if you just want to do this in React, then you will need to define the function in the top level component and then pass it down as props to the child component.
So the MainComponent.js will have a function like
addSubItem = (item) => {
// update the state here
}
Then you will pass it down as props to the component you want to use it in, so pass it to the ListItems.js component by
<ListItems items={this.retieveItems()} addSubItem={addSubItem} />
and then again to the Item.js component
<Item item={item} key={idx} addSubItem={this.props.addSubItem}/>
then in the Item component just call it with this.props.addSubItem
Because it is scoped to the top level component, when you call the function in the child components and pass it an item, it will update the state in the parent component
Look into using Context and the useContext hook if you don’t want to use redux for a simple app.

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

Categories

Resources