In my mongoDB I have documents with nested objects that corresponds to which make, model and year of the motorbike they fit to.
Example:
fits: {
honda: {
crf250: {
1990: true,
1991: true
},
rx400: {
2000: true
}
},
kawasaki: {
ninja: {
2015: true
}
}
}
I need to loop through all the makes that the document stores in fits field (In the example above it would be honda and kawasaki) and than return all the models that exist under the specific make. I am succesfully receiving the array of all the models under the make in my aggregate method.
return(
<ul style={{listStyleType: 'none'}}>
{Object.keys(props.data.fits).map((make, i) => {
if(db !== null && client !== null){
var query = `fits.${make}`;
var pipeline = [
{
$match: {
[query]: {
'$exists': true,
'$ne': {}
}
},
},
{
$group: {
_id: `$${query}`,
}
}
]
client.auth.loginWithCredential(new AnonymousCredential()).then((user) => {
db.collection('products').aggregate(pipeline).toArray().then((models)=>{
return <Make
style={{border: '1px solid grey'}}
mongoClient={props.mongoClient}
id={props.data._id}
key={i}
make={make}
data={props.data}
_handleDeleteMake={handleDeleteMake}
_updateRowData={props._updateRowData}
_models={models}
>
</Make>
}).catch(e=>console.log(e))
})
}
})}
</ul>
)
However after the call I need to render the makes. It should look something like this:
Next to the orange plus I want to show the list of all the other models that exists under the specific make so I don't have to repeat in writing the model again if its exists already and I can just click on it.
However rendering Make inside the async I am left with blank:
Now from what I understand is that the render finished before the async function finished that is why it simply renders empty list, but I don't really know how should I approach this problem. Any suggestions?
I don't think it's possible for you to render a React element in that async way. When React try to render your element that is inside the ul tags, because you are using async, at the time of DOM painting, there is nothing for React to render. Thus React render blank.
After the async is resolved, React won't re-render because React doesn't know that there is a new element being added in. Thus even when you actually have that element, since React doesn't re-render, you won't see that element in the app
Why does this happen? Because React only re-render when there are certain "signal" that tells React to re-render. Such is state change, props change, hooks call, etc. What you did doesn't fall into any of those categories, so React won't re-render. This is the same reason why you don't directly change the component state and instead must use method like setState to change it.
Related
In my NextJS application interfaced with a MongoDB API back-end - managed via GraphQL, I'm trying to implement the Apollo fetchMore feature, in order to update a component responsible to load more items from a data collection.
On page rendering, the component itself shows a "gallery" of 10 elements as its native functionality, populated via a GraphQL starting query. Then, I included a "load more" button to trigger the fetchMore function. The UX expects that if the user clicks the proper button, more 10 elements will going to be loaded in addition of the previous 10 - basically a classical async-infinite loading example.
By inspecting the app, I notice that both the queries are being returned successfully - the initialization one and the "load more 10 items" too managed by fetchMore - but the latter, after its execution, triggers the component's update that it's being re-initialized with the starter query instead of the fetchMore one.
To clarify it: on "load more" click, instead to see the next 10 gallery elements loaded - so to finally display a total of 20 - the component refreshes and displays the starter 10 elements, like its starting initialization - totally ignoring the fetchMore action, even if this one is being called, executed and received back with a populated 200 response.
Because this is my very first time in using it, I don't know if I'm missing something in my implementation, or I need to fix something. Anyway, here it goes:
Due to various reasons, I'm running the query in a parent component, then I pass the data as props to a child one:
Parent
// Initialization, etc.
[...]
const {loading: loadingIndex, error: errorIndex, data: dataIndex, fetchMore: fetchMoreIndex} = useQuery(ARTICLE_QUERY.articles.indexArticles, {
// Last 30 days
variables: {
live: live,
limit: 10
}
});
// Exception check
if (errorIndex) {
return <ErrorDb error={errorIndex} />
}
// DB fetching check
if (loadingIndex) {
return (
<section className="index-articles">
<h6>Index - Articles</h6>
<aside className="articles__loading">
<h6>Loading</h6>
</aside>
</section>
);
}
const articles = dataIndex.queryArticleContents;
return (
<IndexArticles labels={props.labels} articles={articles} fetchMore={fetchMoreIndex} />
);
Child
// Initialization, etc.
[...]
let limit = 10; // My query hypothetically limiter
const IndexArticles = (props) => {
useEffect(() => {
// This is a getter method responsible to manage the ```fetchMore``` response
getArticles(props.articles, props.fetchMore);
});
return (
<>
// Component sections
[...]
// Load button
{props.fetchMore &&
<button className="articles__load" title={props.labels.index.title} tabIndex={40}>{props.labels.index.cta}</button>
}
</>
);
function getArticles(articles, fetchMore) {
// Yes, I'm using jQuery with React. Just ignore it
$('.articles__load').on('click tap', function(e) {
e.preventDefault();
$(this).addClass('hidden');
$('.articles__loading').removeClass('hidden');
fetchMore({
variables: {
// Cursor is being pointed to the last available element of the current collection
lastLoaded: articles.length,
limit: limit += 10
},
updateQuery: (prev, {fetchMoreResult, ...rest}) => {
$('.articles__loading').addClass('hidden');
$(this).removeClass('hidden');
if (!fetchMoreResult) {
return prev;
}
return {
...fetchMoreResult,
queryArticleContents: [
...prev.queryArticleContents,
...fetchMoreResult.queryArticleContents
]
}
}
});
});
}
Anyone have experience with it or had experienced this case before?
Thanks in advance for the help
As suggested on the official community, my configuration was missing about the notifyOnNetworkStatusChange: true in the query options, which is responsible to update the component and append the new data.
By changing the code in this way:
Parent
const {
loading: loadingIndex,
error: errorIndex,
data: dataIndex,
// Add networkStatus property too in order to use notifyOnNetworkStatusChange properly
networkStatus: networkStatusIndex
fetchMore: fetchMoreIndex} = useQuery(ARTICLE_QUERY.articles.indexArticles, {
// Last 30 days
variables: {
live: live,
limit: 10
},
// Important for component refreshing with new data
notifyOnNetworkStatusChange: true
});
The problem has been solved.
It must be pretty regular issue.
I'm passing props down to the children and I'm using it there to request to the endpoint. More detailed: I'm clicking on the list item, I'm checking which item was clicked, I'm passing it to the child component and there basing on prop I passed I'd like to request certain data. All works fine and I'm getting what I need, but only for the first time, ie. when refreshing page incoming props are gone and I cannot construct proper URL where as a query I'd like to use the prop value. Is there a way to preserve the prop so when the page will be refresh it will preserve last prop.
Thank you!
(You might want to take a look at: https://github.com/rt2zz/redux-persist, it is one of my favorites)
Just like a normal web application if the user reloads the page you're going to have your code reloaded. The solution is you need to store the critical data somewhere other than the React state if you want it to survive.
Here's a "template" in pseudo code. I just used a "LocalStorage" class that doesn't exist. You could pick whatever method you wanted.
class Persist extends React.Component {
constuctor(props) {
this.state = {
criticalData = null
}
}
componentDidMount() {
//pseudo code
let criticalData = LocalStorage.get('criticalData')
this.setState({
criticalData: criticalData
})
}
_handleCriticalUpdate(update) {
const merge = {
...LocalStorage.get('criticalData')
...update
}
LocalStorage.put('criticalData', merge)
this.setState({
criticalData: merge
})
}
render() {
<div>
...
<button
onClick={e => {
let update = ...my business logic
this._handleCriticalUpdate(update) //instead of set state
}}
>
....
</div>
}
}
By offloading your critical data to a cookie or the local storage you are injecting persistence into the lifecycle of the component. This means when a user refreshes the page you keep your state.
I hope that helps!
My instinct tells me no, but I'm having difficultly thinking of a better way.
Currently, I have a component that displays a list of items. Depending on the provided props, this list may change (i.e. filtering change or contextual change)
For example, given a new this.props.type, the state will be updated as follows:
componentWillReceiveProps(nextProps) {
if (nextProps.type == this.state.filters.type) return
this.setState({
filters: {
...this.state.filters,
type: nextProps.type,
},
items: ItemsStore.getItems().filter(item => item.type == nextProps.type)
})
}
This is all fine and good, but now my requirements have changed and I need to add a new filter. For the new filter, I must execute an API call to return a list of valid item ids and I only want to display items with these ids in the same list component. How should I go about this?
I had thought about calling the appropriate action from componentWillReceiveProps, but that doesn't seem right.
componentWillReceiveProps(nextProps) {
if (nextProps.type == this.state.filters.type && nextProps.otherFilter == this.state.filters.otherFilter) return
if (nextProps.otherFilter != this.state.filters.otherFilter) {
ItemsActions.getValidIdsForOtherFilter(nextProps.otherFilter)
// items will be properly updated in store change listener, onStoreChange below
}
this.setState({
filters: {
...this.state.filters,
type: nextProps.type,
otherFilter: nextProps.otherFilter,
},
items: ItemsStore.getItems().filter(item => item.type == nextProps.type)
})
},
onStoreChange() {
let validIds = ItemsStore.getValidIds()
this.setState({
items: ItemsStore.getItems().filter(item => item.type == this.state.filters.type && validIds.indexOf(item.id) > -1)
})
}
Update 22nd January 2018:
Recently an RFC-PR for React was merged, which deprecates componentWillReceiveProps as it can be unsave when used in the upcoming async rendering mode. An example for this can be calling flux actions from this lifecycle hook.
The correct place to call actions (i.e. side effects) is after React is done rendering, so that means either componentDidMount or componentDidUpdate.
If the intention of the action is to fetch data, React might support a new strategy for these things in the future. In the meantime it's safe to stick to the two mentioned lifecycle hooks.
Background
I am building an app with the following details
react
react-router
redux
it is universal javascript
node js
Problem
When routing with the Link tag from component to component it works perfectly. It calls the data that the component requires and renders the page. But when I click on a Link that uses the same component as the current one all I see is the url change.
Attempts
Things I have tried to get this to work.
Attempt 1
So far I have tried the steps in this question but the solution wont work for me. This was the code I implemented
componentWillReceiveProps(nextProps) {
if (nextProps.article.get('id') !== this.props.article.get('id')) {
console.log('i got trigggerd YESSSSSSSSSSSSSSS');
}
}
But the variable nextProps is always the same as the current props.
Attempt 2
I decided to call the same code I use in componentWillMount but that didn't work either.
componentWillMount() {
let { category, slug } = this.props.params;
this.props.loadArticleState({ category, slug });
}
It just creates an infinite loop when I put this into componentWillReceiveProps.
Conclusion
I belief the problem is clicking the link never calls the data associated with it. Since the data is loaded with
static fetchData({ store, params }) {
let { category, slug } = params;
return store.dispatch(loadArticleState({ category, slug }));
}
Any help is appreciated.
Solution I Used
I created a function to test if the previous data is the same as the changed data.
compareParams(prevProps, props) {
if (!prevProps || typeof prevProps.params !== typeof props.params) {
return false;
}
return Object.is(props.params, prevProps.params);
}
So this tests
are there any previous props?
and then if the props are equal to the previous props?
then return false if there are if this is the case
if not then we see compare props and previous props parameters
In ComponentDidUpdate
In the compoonentDidUpdate we use this function to determine if the data should be updated
componentDidUpdate(prevProps) {
if (this.compareParams(prevProps, this.props)) {
return;
}
this.props[this.constructor.reducerName](this.props.params);
}
Conclusion
This code updates the body of a page that uses the same react component if it receives new data.
maybe you can try use onChange event on Route component, check Route API and then signal to child component that refresh is needed...
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.