React best practise / using parent component instance - reactjs

let's imagine data that would look like that :
{
{
title: "great track",
tags: ["techno"]
},
{
title: "superb track",
tags: ["house", "90s"]
},
...
}
I render that in an html table, I have a component for the whole table, and a sub component for the tr (aka song title and tracks). so on each line I want to allow the users to be able to access a popup in which they can choose one or more tags for a song. I did it using reactstrap, it works ok.
I'm just a little disappointed by performance, it's quite ok, once it's built, but I saw how much longer it was to load when I added my modal on each line. So my first reflex, was to built only one modal in the parent component, and then use it from the sub component, and then I read articles on how, "one should not use the parent instance because it's bad"(tm).
I understand the point about dataflow, but in my example, having a modal waiting on each line while I'm sure I will never have two at the same time on screen feels like a waste of ressources.
Can anyone point me to an elegant way of building that kind of feature, in this particular context ?

Lifting state up to the parent component is a common practice in react, you can read articles in official documentation https://reactjs.org/docs/lifting-state-up.html
But there is one problem, when you use setState in your parent component, your songs table will render again and again, so you should care about it. One of the way is creating PureComponent for Songs table(if there is no changing in props, this component will not rerender)
I think, the code below is one of the way;
class Parent extends React.Component{
state={
tags: [],
songs: {
title: "great track",
tags: ["techno"]
},
{
title: "superb track",
tags: ["house", "90s"]
}
}
handlePopup(data){
this.setState({tags: data});
}
render(){
const {tags, songs} = this.state;
cons isShowModal = tags && tags.length
return (
<div>
{isShowModal && <Modal data={tags} />}
<SongsList data={songs} />
</div>
)
}
}
class Parent extends React.PureComponent{
render(){
const {data} = this.props;
return (
<table><tbody>...render songs data</tbody></table>
)
}
}

Of course using modal in child rows is a waste of resources. you need to add a modal to parent and use props to show/hide it. also, you should use the key prop for your list items.
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there’s a difference.
but when children have keys, React uses the key to match children in the original tree with children in the subsequent tree.
it's good for performance.

Related

ReactJS: How to render a collection of objects

So I'm quite new on web development last couple of days. I come from c++ background and I can't wrap my head through all the principles of reactjs. I have 2 classes. The child class called JobAd should render some information that it got from props.
export default class JobAd extends Component {
constructor(props) {
super(props);
this.state ={
index: props.index,
id: props.jobId,
name: props.name,
description: props.description,
location: props.location,
adress: props.adress,
alreadyApplied: props.alreadyApplied,
open: false,
// toggleJob: props.toggleJob,
};
this.toggleJob = props.toggleJob;
}
render() {
return (
<div className={`${styles.jobAd} d-flex` + "job " + (this.state.open ? 'open': '')} key={this.state.index} onClick={() => this.toggleJob(this.state.index)}>
<div className={`${styles.jobTitle}`}>
{this.state.location} - {this.state.name}
</div>
<div className={`${styles.jobDetails}`}>
<div className={`${styles.jobDescription}`}> {this.state.description}</div>
<div className={`${styles.jobAdress}`}>{this.state.adress}</div>
<ApplyButton jobId= {this.props.id} alreadyApplied = {this.props.alreadyApplied}/>
</div>
</div>
)
}
}
The second class, queries a mongoDB db and creates jobAd objects populating them from the info gotten from db.
class JobExplorer extends React.Component
{
...
result.data.jobs.forEach(job => {
var find = job.employees.find(obj => obj === userId);
if (!(find === undefined)) {
alreadyApplied = true;
}
var toPush = new JobAd ({
index: i,
id:job._id,
description:job.description,
name:job.name,
location:job.locationName,
adress:job.locationAdress,
alreadyApplied:alreadyApplied,
open:false,
toggleJob: this.toggleJob.bind(this)
});
jobList2.push(toPush);
console.log("look");
console.log(jobList2)
});
this.setState({
jobList: jobList2
})
this.setState({
error: null,
jobs: result.data.jobs
});
...
render()
{
console.log("look2");
console.log(this.state.jobList);
return (
<div><Navigation />
{this.state.jobList}
</div>
);
}
But I am faced with the following error which I cannot find a fix for.
Error: Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state, toggleJob}). If you meant to render a collection of children, use an array instead.
How should I instantiate those objects so I could render them using the "architecture" I wrote. Is there a fundamental flaw that I have in my classes?
The below snippet doesn't work because new will return an object (this) not the react component.
So, instead of
var toPush = new JobAd({
index: i,
id: job._id,
...
});
jobList2.push(toPush);
you can do this
var toPush = <JobAd
index={i}
id={job._id}
...
/>;
The above snippet works because <JobAd ... /> is converted to React.createElement(JobAd, ... ). However, you still shouldn't do it like this. since there are a lot of better ways to do this. one of them is:
save just the data in joblist and then render the data list on JobAd component
like below:-
render(){
return this.state.joblist.map((job, i) => (
<JobAd
key={job._id}
index={i}
...
/>
));
}
The key is a really important thing. Read about it: https://reactjs.org/docs/lists-and-keys.html
Things that could be improved:-
Don't copy props in the state as you are doing in JobAd class instead directly render the props.
Don't call setState twice as in JobExplorer. you could set all the keys in
setState at the same time. since that would render the component twice.
Suggestions:-
You should avoid using var as that might cause some issues here.
since, you are just a starter, try using functional component first. they are
quite easier to grasp
You seem to have a misconception about state/props in React and web development. It's very normal; I learned python and Java first and many tutorials seem to assume that people just know this already.
"State" in generally refers to variables containing/referring to values that can change without a page refresh in your application. If you know a value is not going to change, it does not need to be held in state. Storing it in a normal variable is exactly what you should do.
"Props" is just another word for arguments that are passed to React components. There's more to it in reality, but as a beginner, that's all you need to really know for now.
So in your job add, things like name, address, jobs, description shouldn't go in state because they aren't going to change as a result of user interaction or for any other reason, unless the underlying data they are loaded from changes, but then that wouldn't be handled by React but instead by the API that your app gets data from. They should just be rendered, so refer to them like this.props.address in your render method. The value for open, however, need to be in state, because that definitely can change.
As for the error, it looks like you are not calling JobAd correctly. You need to use the syntax <Job Ad/> rather than new JobAd...that won't work in React.
I would recommend doing a tutorial to get the basics down.

React Stateless Components: Interacting with their output and appearance

I have looked around for an answer to this - the closest I found being this question - but there is I think a significant difference in my case (the fact that it starts to get into the parent holding the state of its children's... children) which has finally lead to me asking for some clarification.
A very simple example of what I mean is below (and will hopefully better illustrate what I'm asking):
Suppose we have a bunch of book documents like
bookList = [
{
title: "book 1",
author: "bob",
isbn: 1,
chapters: [
{ chapterNum: 1, chapterTitle: "intro", chapterDesc: "very first chapter", startPg: 2, endPg: 23 },
{ chapterNum: 2, chapterTitle: "getting started", chapterDesc: "the basics", startPg: 24, endPg: 45 }
]},
{
title: "book 2" ... }
]
So main point being these embedded objects within documents that could be very long and as such may be collapsed / expanded.
And then here is a rough sample of code showing the components
class App extends Component {
constructor(props) {
super(props);
this.state = {
books: bookList,
focusBook: null
}
this.updateDetailDiv = this.updateDetailDiv.bind(this);
}
updateDetailDiv(book) {
this.setState(
{ focusBook: book}
);
}
render() {
return(
<BookList
bookList = {this.state.books}
updateDetailDiv = { this.updateDetailDiv }
/>
<BookDetail
focusBook = { this.state.focusBook }
/>
);
}
}
const BookList = props => {
return (
props.bookList.map(item=>
<li onClick={()=> props.updateDetailDiv(item)}> {item.title} </li>
)
);
}
const BookDetail = props => {
return (
<div className="bookDetails">
{ props.focusBook != null
? <div>
{props.focusBook.title},
{props.focusBook.author},
{props.focusBook.isbn}
Chapters:
<div className="chapterList">
{ props.focusBook.chapters.map(item=>
<span onClick={()=>someFunction(item)}>{item.chapterNum} - {item.chapterName}</span>
)}
</div>
<div id="chapterDetails">
This text will be replaced with the last clicked chapter's expanded details
</div>
</div>
: <div>
Select A Book
</div>
})
}
someFunction(item) {
document.getElementById('chapterDetails').innerHTML = `<p>${item.chapterDesc}</p><p>${item.startPg}</p><p>${item.endPg}</p>`;
}
So my problem is that i'm not sure what the best approach is for handling simple cosmetic / visual changes to data in functional stateless components without passing it up to the parent component - which is fine and makes sense for the first child - but what happens when many children will have their own children (who may have their own children) --> all requiring their own rendering options?
For example - here the App component will re-render the DetailDiv component (since the state has changed) - but I don't want the App also handling the DetailDiv's detailed div. In my example here its all very simple but the actual application I'm working on has 2 or 3 layers of embedded items that - once rendered by App - could realisticially just be modified visually by normal JS.
SO in my example you'll see I have a someFunction() in each Chapter listing - I can make this work by writing a separate simple 'traditional JS DOM function' (ie: target.getElementById or closest() -- but i don't think i'm supposed to be using normal JS to manipulate the DOM while using React.
So again to summarize - what is the best way to handle simple DOM manipulation to the rendered output of stateless components? Making these into their own class seems like overkill - and having the 'parent' App handle its 'grandchildren' and 'great-grandchildren's state is going to be unwieldy as the Application grows. I must be missing an obvious example out there because I haven't seen much in the way of handling this without layers of stateful components.
EDIT for clarity:
BookDetail is a stateless component.
It is handed an object as a prop by a parent stateful component (App)
When App's state is changed, it will render again, reflecting the changes.
Assume BookDetail is responsible for displaying a lot of data.
I want it so each of the span in BookDetail, when clicked, will display its relevant item in the chapterDetail div.
If another span is clicked, then the chapterDetail div would fill with that item's details. (this is just a simple example - it can be any other pure appearance change to some stateless component - where it seems like overkill for a parent to have to keep track of it)
I don't know how to change the UI/appearance of the stateless component after it is rendered without giving it state OR making the parent keep track of what is essentially a 'substate' (since the only way to update the appearance of a component is to change its state, triggering a render).
Is there a way to do this without making BookDetail a stateful component?
You can add a little bit of simple state to functional components to track the selected index. In this case I would store a "selected chapter index" in state and then render in the div the "chapters[index].details", all without manipulating the DOM which is a React anti-pattern.
The use-case here is that the selected chapter is an internal detail that only BookDetail cares about, so don't lift this "state" to a parent component and since it is also only relevant during the lifetime of BookDetail it is rather unnecessary to store this selected index in an app-wide state management system, like redux.
const BookDetail = ({ focusBook }) => {
// use a state hook to store a selected chapter index
const [selectedChapter, setSelectedChapter] = useState();
useEffect(() => setSelectedChapter(-1), [focusBook]);
if (!focusBook) {
return <div>Select A Book</div>;
}
const { author, chapters, isbn, title } = focusBook;
return (
<div className="bookDetails">
<div>
<div>Title: {title},</div>
<div>Author: {author},</div>
<div>ISBN: {isbn}</div>
Chapters:
<div className="chapterList">
{chapters.map(({chapterName, chapterNum}, index) => (
<button
key={chapterName}
onClick={() => setSelectedChapter(selectedChapter >= 0 ? -1 : index)} // set the selected index
>
{chapterNum} - {chapterName}
</button>
))}
</div>
// if a valid index is selected then render details div with
// chapter details by index
{chapters[selectedChapter] && (
<div id="chapterDetails">
{chapters[selectedChapter].details}
</div>
)}
</div>
</div>
);
};
DEMO
There is some approaches you can do to solve this problem.
First, you don't need to create some class components for your functional components, instead, you can use react hooks, like useState so the component can control it's own content.
Now, if you don't want to use React Hooks, you can use React Redux store to manage all your states: you can only change the state values using the Redux actions.
Happy coding! :D

ReactJS variables problems. It's updating variables that are not being touched

I'm working on a ReactJS app and i'm a new comer.
I have a Component like this
class Type extends React.Component {
constructor(props) {
super(props);
this.state = {
name : props.type.name,
description : props.type.description,
price : props.type.price,
imageList : props.type.images,
mode : 'view',
// i'm cloning the whole object
clone : props.type
};
}
handleDeleteImage(event) {
const imageId = event.target.getAttribute('data-imageId');
// get the current imageList of this Component
var imageList = this.state.imageList;
// checking the length of 2 image list before removing the
// targeted image
console.log(imageList.length) // displays 3
console.log(this.state.clone.images.length) // displays 3
// remove the targeted imageId
imageList.splice(imageId, 1);
// checking the length of 2 image list after removing the
// targeted image
console.log(imageList.length) // displays 2
console.log(this.state.clone.images.length) // displays 2
}
}
So what i'm doing here is i want to clone the object so when the user changes there mind and doesn't want to make changes anymore, they can hit the cancel button and everything is back to the state they were before (i have a function to handle this as well. I set the fields -name, description, price- to the values of the clone)
But as you can see, i didn't touched the image list in the clone at all still it got changed anyway.
Am i doing anything wrong here?
Thank you for any help.
Hey guys! So I realized that the concept I used in this service is not so efficient.
Like #Michael McQuade said, I should control the data in one flow only which is changing the data in the parent Component, not the child ones. I also reviewed the ReactJS Documentation and I can see why.
But with that being said. Let's say I'm working on a Component which has lots of Child-Component, does that mean I have to callback all the way up to the Parent Component to make changes in the Child one? And does that mean i must have multiple handlers in the Parent one that will be passed down to the Child that needs them?
I hope my question doesn't border you guys. Thanks!
You're using state and props together in a way I wouldn't recommend.
Instead of trying to make a copy of the props and storing it as state, make a stateless function and pass down a function which handles the deletion.
Here is an example:
class Child extends React.PureComponent {
render () {
return (<button onClick={this.props.handleBye}>{this.props.text}</button>)
}
}
class Parent extends React.PureComponent {
state = {
text: "Hello"
}
handler = () => {
this.setState({text: "bye"})
}
render() {
return (<Child text={this.state.text} handleBye={this.handler} />)
}ˆ
}
ReactDOM.render(<Parent />, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This line will not clone an object, rather it will create a reference
clone : props.type
To clone you can use various techniques (depending on your need) one simple one would be
clone: Object.assign({}, props.type)
beware that this will only create a shallow copy of the object.
To create a deep copy you can use
clone: JSON.parse(JSON.stringify(props.type))
this is an easy technique but it is slow and will not copy dates correctly.
If you need fast and reliable deep clone you better search for something else that suits your needs (maybe a library like lodash).
this.state.clone is just a reference to the props.type object. So when you use splice() you change the contents of the array and therefore "mutate" props.type.
If you really want to clone the object do it like that:
this.state = {
clone: {...props.type} // create a new object and spread the props.type object properties
}
You can read more about the spread operator here

Reconciliation in React detailed explanation

I am new to react JS. Can anyone explain reconciliation exactly how it works. I have tried understanding it from react official site but didn't got it.
This is how I understand :
You would agree that react makes thing simple and faster using components .
With JSX we can make things easier for user-defined components .
End of the day all of it gets translated to pure JavaScript (I assume you understand how React.createElement works)with function calls holding other function calls as its arguments/properties holding yet other function calls and so on ..
Anyway nothing for us to worry about as react does this on its own internally .
But how does this gives us an UI ?
Why it is faster from other UI libraries ?
<-- ALL HAIL ReactDOM library and the render method -->
An ordinary ReactDOM call looks like this :
// I have avoided the usage of JSX as its get transpiled anyway
ReactDOM.render(
React.createElement(App, { //if any props to pass or child }), // "creating" a component
document.getElementById('#root') // inserting it on a page
);
Heard about VirtualDOM ? { yes : 'Good'} : { no : 'still Good'} ;
The React.createElement construct element object with type and props based on the components we have written and place the child elements under a children key inside props.
It recursively does this and populates a final object which is ready to be converted to HTML equivalent and painted to the Browser.
This is what VirtualDOM is, which resides in reacts memory and react performs all its operation on this rather on actual Browser DOM .
It looks something like this:
{
type: 'div',// could be other html'span' or user-diff 'MyComponent'
props: {
className: 'cn',
//other props ...
children: [
'Content 1!', // could be a component itself
'Content 2!', // could be a component itself
'Content n!', // could be a component itself
]
}
}
After a Virtual DOM object is built, ReactDOM.render will transform it into a DOM node our browser can paint the UI according to those rules:
If a type attribute holds a string with a tag name—create a tag with all attributes listed under props.
If we have a function or a class under type—call it and repeat the process recursively on a result.
If there are any children under props—repeat the process for each child one by one and place results inside the parent’s DOM node.
The Browser paints it to the UI , this is an expensive task .
React is very smart to understand this.
Updating the component means creation of a new object and paint to UI. Even if a small change is involved it will make the whole DOM tree recreated .
So how do we make the Browser never have to create DOM each time rather paint only the necessary things.
This is where we need Reconciliation and the diffing algorithm of React ..
Thanks to react we don't have to do it our self manually , its taken care of internally here is a nice article to understand deeper
Now you can even refer the official React docs for Reconsiliation
Few points worth noting :
React implements a heuristic O(n) algorithm based on two assumptions:
1) Two elements of different types will produce different trees.
2) The developer can hint at which child elements may be stable across different renders with a key prop.
In practice, these assumptions are valid for almost all practical use cases.
If these are not met it will cause performance issues.
I am just copy Pasting few other points just to give a idea how its done :
Diffing :
When diffing two trees, React first compares the two root elements. The behavior is different depending on the types of the root elements.
Scenario 1: type is a string, type stayed the same across calls, props did not change either.
// before update
{ type: 'div', props: { className: 'cn' , title : 'stuff'} }
// after update
{ type: 'div', props: { className: 'cn' , title : 'stuff'} }
That is the simplest case: DOM stays the same.
Scenario 2: type is still the same string, props are different.
// before update:
{ type: 'div', props: { className: 'cn' } }
// after update:
{ type: 'div', props: { className: 'cnn' } }
As type still represents an HTML element,React looks at the attributes of both, React knows how to change its properties through standard DOM API calls, without removing the underlying DOM node from a DOM tree.
React also knows to update only the properties that changed. For example:
<div style={{color: 'red', fontWeight: 'bold'}} />
<div style={{color: 'green', fontWeight: 'bold'}} />
When converting between these two elements, React knows to only modify the color style, not the fontWeight.
///////When a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element, and calls componentWillReceiveProps() and componentWillUpdate() on the underlying instance.
Next, the render() method is called and the diff algorithm recurses on the previous result and the new result.
After handling the DOM node, React then recurses on the children.
Scenario 3: type has changed to a different String, or from String to a component.
// before update:
{ type: 'div', props: { className: 'cn' } }
// after update:
{ type: 'span', props: { className: 'cn' } }
As React now sees that the type is different, it would not even try to update our node: old element will be removed (unmounted) together with all its children.
It is important to remember that React uses === (triple equals) to compare type values, so they have to be the same instances of the same class or the same function.
Scenario 4: type is a component.
// before update:
{ type: Table, props: { rows: rows } }
// after update:
{ type: Table, props: { rows: rows } }
“But nothing had changed!”, you might say, and you will be wrong.
If type is a reference to a function or a class (that is, your regular React component), and we started tree reconciliation process, then React will always try to look inside the component to make sure that the values returned on render did not change (sort of a precaution against side-effects). Rinse and repeat for each component down the tree—yes, with complicated renders that might become expensive too!
To make sure such things come clean:
class App extends React.Component {
state = {
change: true
}
handleChange = (event) => {
this.setState({change: !this.state.change})
}
render() {
const { change } = this.state
return(
<div>
<div>
<button onClick={this.handleChange}>Change</button>
</div>
{
change ?
<div>
This is div cause it's true
<h2>This is a h2 element in the div</h2>
</div> :
<p>
This is a p element cause it's false
<br />
<span>This is another paragraph in the false paragraph</span>
</p>
}
</div>
)
}
}
Children =============================>
we also need to account for React’s behavior when an element has more than one child. Let’s say we have such an element:
// ...
props: {
children: [
{ type: 'div' },
{ type: 'span' },
{ type: 'br' }
]
},
// ...
And we want to shuffle those children around:
// ...
props: {
children: [
{ type: 'span' },
{ type: 'div' },
{ type: 'br' }
]
},
// ...
What happens then?
If, while “diffing”, React sees any array inside props.children, it starts comparing elements in it with the ones in the array it saw before by looking at them in order: index 0 will be compared to index 0, index 1 to index 1, etc.
For each pair, React will apply the set of rules described above.
React has a built-in way to solve this problem. If an element has a key property, elements will be compared by a value of a key, not by index. As long as keys are unique, React will move elements around without removing them from DOM tree and then putting them back (a process known in React as mounting/unmounting).
So Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
Because React relies on heuristics, if the assumptions behind them are not met, performance will suffer.
When state changes: =========================================>
Calling this.setState causes a re-render too, but not of the whole page, but only of a component itself and its children. Parents and siblings are spared. That is convenient when we have a large tree, and we want to redraw only a part of it.
Reconciliation in the context of React means to make React's virtual DOM tree consistent with the real DOM tree of your browser. This happens during (re-)rendering
The key point is that there is no guarantee that a specific element of React's virtual DOM refers to the same DOM node of your browser for its complete lifecycle. The reason for this is React's approach to update the DOM efficiently. You can use the special key property to solve this issue, if a component contains dynamic or stateful children.

How to use the component string returned from server in React via AJAX

All:
I am pretty new to React, I am trying to render a component from the string return by Server side ReactDomServer.renderToString(), could anyone give me a working patrn or example to do this in AJAX?
A case will be:
One the init page, there is a dropdown, you choose different type of componnet, then it will submit AJAX request to server, then server return according string, then the page will render that component on it.
Thanks
From the comments on your question, it sounds like what you're looking to do is dynamically render a UI.
Your request was for an example of a drag-and-drop rendering workflow, but that would stray too far from your question. It's important that we first tease out the many components and then focus on the one that's interesting for this question. We have a data layer and server-side responsible for storing information, business logic for determining how components should render where and when, interaction paradigms like drag and drop that work within these rules, and the rendering of components based on them.
All of these are separate concerns that must be considered independently. For example, drag and drop is one way to add components, but it is likely to not be the only way, so why couple the two? That leaves us with just rendering dynamic components, which I shall consider here. I'll be using ES2015 syntax to make the code cleaner.
First, we have a main component that does the wrapping:
const Renderer = React.createClass({
render () {
// ...
},
});
ReactDOM.render(
<Renderer layout={layout} />,
document.getElementById( 'app' )
);
Now let's consider the components you mentioned, which will be pure:
const Button = ({ text }) => (
<button>{text}</button>
);
const Input = ({ type = "text", placeholder }) => (
<input type={type} placeholder={placeholder} />
);
And some container for available components (which would likely also have metadata and rules about each):
const Components = {
Button,
Input,
};
And now let's assume we have a configuration defined in json:
{
"name": "My Interface",
"layout": [
{ "id": 123, "component": "Input", "placeholder": "keywords..." },
{ "id": 456, "component": "Button", "value": "Search!" },
],
}
This is highly simplified, but you can imagine this document showing all properties for a deeply nested UI, perhaps sporting different types of containers like rows and columns. Now we can assume the JSON property layout is the layout property passed to the Renderer above. Now our render function can look like this (highly simplified):
render () {
const children = this.props.layout.map( ({ component, ...props }) => {
const Component = Components[ component ];
return <Component {...props} />
});
return (
<div className="component-view">
{children}
</div>
);
}
Whenever the model changes, we would re-render the component tree and see what we should. There is a lot that would have to go into something like this to get a full UI editor - that's a massive undertaking. But with proper design principles and separation of concerns, it's at least doable.
To return to drag and drop briefly, if we were to drag and drop, we would note its place and insert the component into the tree however made sense based on the component and the state of the item onto which it's dropped, etc. The result of the operation, assuming it was successful, would be a mutated layout tree, which triggers a re-render.
Note: I completely ignored performance considerations.

Resources