This is a general question. I have a redux saga yielding calls that update the store every x mins and show the store gets updated appropriately in the redux dev tool. In the render method of my component if I click before the data I will get a spinner and if I click after the component will render; HOWEVER, in the components class the life cycle "componentWillUpdate" or "componentWillReceiveProps" shows the connected piece in redux store as undefined in either method yet the render is able to pass the correct props; what the cluck? I'll head back to the docs but this seems odd.
...
//dont usually use this for redux
componentWillReceiveProps(){
console.log(dailyOperations) // nothing here
}
componentWillUpdate(){
console.log(dailyOperations) // nothing here
}
render(){
if (dailyOperations === undefined) {
return (<SpinnerThing />)
else
return (<SomeDisplayComponent data={dailyOperations} />) //Data is here
}
I couldn't at the time account for it but now it makes sense and if it helps anyone else great. It will not be there on the update for the connected redux state but through "componentWillUpdate(nextProps)" & nextProps will have it.
Related
I'm having issue getting the React Fabric UI DetailsList to work with React Hooks. It renders, but the selection part does not. Whenever, you select a row, I expect the count of the selection to be updated. However, i'm not seeing that. It looks like the selection component items never get updated even thou the UI shows it being selected. I'm also seeing the onSelectionChange being triggered when you select a row. Now sure if its because i'm using react hooks or what
I took the provided class example which works:
[Codepen]https://codepen.io/pen/?&editable=true (Example)
Same as the original but stripped down
[Codepen]https://codepen.io/darewreckk/pen/NQRWEd?editors=1111
converted it to a react hook component. I would expect the same thing, but i'm not and can't figure out what's different.
Modified Example with Hooks
[Codepen]https://codepen.io/darewreckk/pen/GVjRNb?editors=1111
Any advice appreciated,
Thanks,
Derek
The selection count is stored on the Selection object that you are creating. You could log it out like so:
const _selection = new Selection({
onSelectionChanged: () => {
console.log('Selected count:' + _selection.count);
}
});
You can set the value of selectionCount and selectionDetails whenever onSelectionChanged is called:
const _selection = new Selection({
onSelectionChanged: () => {
setSelectionCount(_selection.count);
setSelectionDetails(`${_selection.count} items selected`)
}
});
As a side note: If you want selectionDetails to update when selectionCount updates, you can use the useEffect hook:
React.useEffect(() => {
setSelectionDetails(`${selectionCount} items selected`);
}, [selectionCount]);
The above effect will run whenever selectionCount updates, and so in turn update selectionDetails.
It seems a common technic to use PureComponent to improve rendering perf in React. However, it seems not the case when using PureComponent who has children as props.
class App extends React.PureComponent {
render() {
console.log('re-render')
return <div>{this.props.children}</div>
}
}
const render = () => {
ReactDOM.render(
<App>
<div />
</App>,
document.getElementById('app')
)
setTimeout(render, 1000)
}
render()
The result is that console keeps logging 're-render' every 1s. It seems the children(<div />) is the only prop of above App component and never changes, why App still gets re-rendered?
Note: in case of any confusion, the question is the same as, why SCU(shouldComponentUpdate) hook of PureComponent above return true since no props seems changed?
What happen here is you're actually calling ReactDOM.render(), Page (or App, I suppose you have a typo here) component is gonna trigger its render() function regardless of using Component or PureComponent.
The way PureComponent can help to reduce unnecessary rendering is when there is a prop change, PureComponent will do a shallow comparison on this.props and nextProps to determine if this PureComponent needs to call render().
I just made this example for you:
class App extends React.PureComponent {
state = {value: 0}
componentDidMount() {
setInterval(() => {
this.setState({value: Math.random()})
}, 1000)
}
render() {
return (
<div>
<PureChild value="fixed value"/>
<ImpureChild value="fixed value"/>
</div>
)
}
}
class PureChild extends React.PureComponent {
render() {
console.log('rendering PureChild')
return <div>{this.props.value}</div>
}
}
class ImpureChild extends React.Component {
render() {
console.log('rendering ImpureChild')
return <div>{this.props.value}</div>
}
}
Pay attention to this few things:
Both children are receiving a fixed props ("fixed value" string)
Every 1 second, the parent <App /> change value state, thus it re-renders, causing all its children to re-render as well.
But since <PureChild /> is a PureComponent, it does a shallow comparison on its old props and incoming new props, and notices both props are "fixed value", and therefore it doesn't trigger render!
If you run this code and open up console, you'll see only 'rendering ImpureChild' every 1s, but 'rendering PureChild' will only appear once.
console.log(<div /> === <div />) // false
On every rerender of <App />, a new React Element was created by React.createElement(div, null), thus this.props.children will be different from nextProps.children though they look the same in JSX.
In fact, the real problem is that the reference(otherwise value if is primitive type) of props.children changes every time the parent re-renders and React.PureComponent compares props by reference embracing immutability.
Now as per the documentation of ReactDOM
ReactDOM.render() controls the contents of the container node you pass
in. Any existing DOM elements inside are replaced when first called.
Later calls use React’s DOM diffing algorithm for efficient updates.
ReactDOM.render() does not modify the container node (only modifies
the children of the container). It may be possible to insert a
component to an existing DOM node without overwriting the existing
children.
ReactDOM from second time onwards, just updates the React component with the diffing algorithm it uses else where, so Its not the ReactDOM, that causes the re-render then. You can verify this by add a componentWillMount method in the App Component and check that it is only called once
Now coming to the PureComponent. The docs state that
React.PureComponent’s shouldComponentUpdate() only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only extend PureComponent when you expect to have simple props and state
So here is the catch, PureComponent may return false negatives for deeper differences. So when you try to compare this.props.children with nextProps.children for equality you will find that it returns false and hence the re-render is triggered again
Check this CodeSandbox
As per documentation of the React.PureComponent
1). PureComponent implement shouldComponentUpdate() with a shallow props and state comparison, will check whether page needs to be re-render
2). If there is complex objects in props or state, then PureComponent will give false positive result, must have to run force update
3). Change in parent component will not update children, so PureComponent's children should also be PureComponent
I wasn't sure of the best way to word the title, but hopefully the description will clarify.
I have an Angular2 component that uses Redux for state management.
That component uses *ngFor to render an array of small inputs with buttons like this. The "state" is something like this...
// glossing over how I'd get this from the Redux store,
// but assume we have an Immutable.js List of values, like this...
let items = Immutable.fromJS([{value: foo}, {value: bar}, /*...etc*/ })
And the template renders that like so...
<input *ngFor="let item of items, let i = index"
type="text"
value="item.get('value')"
(blur)="onBlur($event, i)">
<button (click)="onClick($event)">Add New Input</button>
When an input is focused and edited, then focus is moved away, the onBlur($event) callback is called, a redux action (ie: "UPDATE_VALUE") is dispatched with the new value.
onBlur($event, index) {
let newValue = $event.target.value;
this.ngRedux.dispatch({
type: "UPDATE_VALUE",
value: {index, newValue}
});
}
And the reducer updates the value (using Immutable.js):
case "UPDATE_VALUE": {
let index = getIndex(action.value.index); // <-- just a helper function to get the index of the current value.
return state.updateIn(["values", index], value => action.value.newValue);
}
The state is updated, so the component is re-rendered with the updated value.
When the button next to the input is clicked, the onClick($event) callback is fired which dispatches a different redux action (ie: "ADD_VALUE"), updates the state, and the component is re-rendered with a new blank input & button.
The problem comes up when the input is focused (editing) and the user clicks the button. The user intended to click the button, but because they happened to be focused on the field, it doesn't behave as expected. The (blur) event is fired first, so the input onBlur callback is fired, redux action dispatched, state updated, component re-rendered. BUT, the button (click) is lost, so the new input isn't created.
Question:
Is there a way to keep track of the click event and trigger the second action after the re-render? Or, is there a way to somehow chain the redux actions so they happen in sequence before the re-render?
Side-note - I've tried changing the (click) event to use (mousedown) which is triggered before the (blur) but that caused Angular (in devMode) to complain that the #inputs to the components were changed after being checked (the state changed during the change detection cycle). I didn't dig into that too deeply yet though. I'm hoping to find a solution using click and blur as is.
Thanks!
I have two components in react native. One class name is firstClass, and the second class is secondClass. "secondClass" is a child view of firstClass. I can submit my data to my firebase from the child view, but on successful submit, I want the view to go back to "firstClass".
I have used event emitters in the past, but those were only using navigatorIOS.
My question is, does the event emitter system listen to global events, or just within their class? And if so, how do I communicate from "secondClass" to "firstClass", that I want to show "firstClass" again.
Thanks!
You can use a callback function and call it from the second component :
in the first component:
//Constructor
....
this.state ={
renderSecondComponent:false
}
....
...Your code
{
this.renderSecondComponent()
}
renderSecondComponent(){
if(this.state.renderSecondComponent){
return(
<SecondComponent callback={()=>{this.setState({renderSecondComponent:false})}}/>
)
}
}
In the second component
....Your code
// when you should go back to the first component
if(this.props.callback){
this.props.callback()
}
I have a preloaded object graph of Backbone collections and models. To initialize my UI I need to make sure the collections are loaded, and then pull some item from them by ID using get(). I want to have a method that accepts a callback which is either called immediately if the collection is loaded, or gets delayed until the collection is loaded.
So far I have the following abomination of a mixin:
window.BackboneReady =
onReady: (cb)->
if #loaded_
console.log "Calling onReady immediately"
cb(#)
else
console.log "Scheduling onReady for later"
#once 'sync', =>
console.log "onReady fired in callback"
#loaded_ = true
cb(#)
however, it only works sometimes (I see the message "Scheduling onReady for later" but my event handler is never executed). Rant: it looks Backbone doesn't even have a basic signal variable to tell me whether the object is synced or not, which seems completely absurd.
What would be the sane way to accomplish this? I don't want to call fetch() every time I want to get() an object from the collection for my UI since this defeats the purpose of holding a preloaded object graph in the first place.
You can try this in your collection to use promises and solve your challenge
initialize: function(){
this.on("request", function(collection, xhr, options){
this.ready = xhr;
});
}
Then you can do
$.when(myCollection.ready).done(function(){
// do things to the collection that is ready
console.log(myCollection.get(5));
});
Or in your collection:
getIfLoaded: function(id){
if(this.ready.state === "resolved"){
return this.get(id);
}
else{
return null;
}
}
For more info on deferred and promises, take a look at http://davidsulc.com/blog/2013/04/01/using-jquery-promises-to-render-backbone-views-after-fetching-data/ and http://davidsulc.com/blog/2013/04/02/rendering-a-view-after-multiple-async-functions-return-using-promises/