Using React Hooks to Show Component Render Time and Date - react-hooks

My application contains a view that is a functional react component. I'm trying to add a timestamp at the bottom of the view displaying the date and time when the user navigated to this view. I'm wondering what would be appropriate way to implement this using hooks. I guess one option would be to use const [date] = useState(new Date()) and simply provide no setter for the state since it would never change. However, this makes me wonder if useState is the appropriate hook to begin with. Should I just do const date = new Date() in the component body or would that have some unexpected side effects?

For what you intend to accomplish, the code below would work just fine:
// solution # 1
function MyComponent() {
const [time, setTime] = useState(new Date());
return (
<div>
View created at {time.toLocaleTimeString()}
</div>
)
}
However, it is important to know that it is not following React rules. That is because we are calling new Date() from inside the component, making it impure. React expects the components to be pure functions. This means that it should return the same thing for the same input. But React cannot guarantee that a function component doesn't have side effects, and that is why that first solution would work just fine.
According to this gist, a better approach for this task would be:
// solution # 2
function MyComponent() {
const [time, setTime] = useState<Date>();
useEffect(() => {
setTime(new Date());
}}, [])
return (
<div>
View created at {time && time.toLocaleTimeString()}
</div>
)
}
Roughly speaking, all side effects should live inside useEffect. So, things like calling an external API or calling impure functions (such as Math.random() or Date.now()) should be there. From my understanding, this solution would be more appropriate.
If we are not obeying React guidelines, the library might not work as advertised. Before version 18, React had only synchronous rendering. This means that the moment the render phase is kicked off (either by a initial render or by a state update), nothing could interrupt it from committing those updates to the browser. In that situation, I don't think the first solution I presented would be problematic. But, in React 18, we have the concurrent mode features by which rendering can be interrupted before the diffs are committed to the screen. With that, React can be more intelligent about how it breaks up all the work it needs to do (for example, we can now define high-priority updates).
In order for these new APIs to work properly, React assumes that the components are pure functions, which means that it has no side effects. These APIs are very new and there are still many more additional features coming in. I am not quite sure about how sensitive they are to function impurity, but I believe our second solution (with all side effects living inside the useEffect) is a better bet for this task.

The following seems to work. Still not sure if this is optimal though.
import React from 'react'
export function View(_props) {
const [viewCreated] = React.useState(Date.now())
return (
<div>
View created {String(new Date(viewCreated))}
</div>
)
}

useState(new Date()) is better. Here is complete component
import React, { useState , useEffect } from 'react'
export const DateTime = () => {
var [date,setDate] = useState(new Date());
useEffect(() => {
var timer = setInterval(()=>setDate(new Date()), 1000 )
return function cleanup() {
clearInterval(timer)
}
});
return(
<div>
<p> Time : {date.toLocaleTimeString()}</p>
<p> Date : {date.toLocaleDateString()}</p>
</div>
)
}
export default DateTime

Related

Vue 3 composable performance issues?

In the following example from the vue 3 documentation, we're creating a composable to track the mouse movement. https://vuejs.org/guide/reusability/composables.html#mouse-tracker-example
export function useMouse() {
const x = ref(0)
const y = ref(0)
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
onMounted(() => {
console.log('mouse event listener bound')
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return { x, y }
}
And then using it within our components to retreive the mouse position on the page.
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
While this works, what I find strange is that using this method you end up with event listeners being attached for each component. So if there are 100 components using useMouse I'll have 100 event listeners attached to the body, each performing the update function which, when added up with other composables, can soon become a performance bottleneck.
Why is the official documentation recommending using this method? Wouldn't a singleton be better suited to this use case? If so how would I create a singleton composable, the vue3 documentation doesn't seem to mention singletons at all.
The document shows that example for the practical purpose only. So the way you are using the composable decides the impact of it on the performance.
If you want to use its function on 100 components at the same time, yes, a singleton would be a good option. But if you want to use the composable in many pages that are separated by the vue router, the composable is totally fine.

React Admin page not rendering correctly

I am new to UI coding and started using react-admin for putting some simple pages. Everything went well and we are able to host pages correctly. But we have noticed random issues where the background image is filling up the entire screen or sometimes the whole page gets reduced to the hamburger menu. I have disabled the registerServiceWorker to stop having my pages in cache. Not sure if this is causing the weird UI behavior.
I don't know why you get those issues, the description is way too generic and it seems you don't have any idea what the problem can be, probably due to being new to the area. Either way the kind of problem you appear to have is probably related to CSS which is a way give style to your page. But React Admin doesn't use CSS directly, you can use it that way, but for more dynamic way to style the page the Material-ui library uses a thing called JSS to apply the styles.
There are many libraries that are being used together in order to produce React Admin, you should have an understanding of the most important ones in order to do something fancy. My advice to you since you are new, and you pretend to use React Admin, first use what React Admin offers and when you feel comfortable using that components and have a general grasp how the framework works, after that start implementing your own components that don't have a direct relation to React Admin but use some of the same libraries of React Admin.
Also check if you are creating a React Admin app using the <Admin> component or are embedding React Admin in another app since the second is more probable to produce bugs.
After some debugging, I think i figured out the cause of this issue. I had a custom button to duplicate a row (basically post a create and route to edit page on the new id). For some reason, the rendering of that button seems to have caused this issue inconsistently. The actual button works fine but causes this inconsistent behavior. Below is the code for that button. Is there any issue with the below?:
export default class DuplicateButton extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = ({ redirect: false });
var redirectPath = '';
}
handleClick = (props) => {
var
{
push, record, resourceName
} = this.props;
let tempRecord = record;
var result = '';
console.log(this.props);
var p = restDataProvider(CREATE, this.props.resource + "/" + tempRecord.id, { data: tempRecord }).then(resp => {
result = resp.data;
let routePath = '/' + this.props.resource + '/' + result.id;
console.log(routePath);
this.redirectPath = routePath;
this.setState({ redirect: true });
return result;
});
}
render() {
if (this.state.redirect) {
console.log('Redirect to Edit page');
return <Redirect push to={this.redirectPath} />;
}
return <Button variant="flat" color="primary" label="Duplicate Entry" onClick={this.handleClick}><DuplicateIcon /></Button>;
}
}

React.PureComponent doesn't work when the Component has children?

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

react-redux together with components status

in a react UI I have a table component. You can edit one row of the table by clicking a edit button or you can add a new record by clicking a "new-record-button". When clicking the edit button an redux-action is triggered which takes the row and sets a visible property of a modal dialog. When the "new-record-button" is clicked an action is triggered which creates a new empty data item and the same modal dialog is triggered.
In the modal dialog I have several text components with onChange method.
in this onChange-method the data-item is written.
When to user clicks a save-button the edited dataItem is saved to the database.
So my code looks like:
const mapStateToProps = (state) => ({
dataItem: state.datItemToEdit || {},
...
});
...
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
dataItem.carId = text;
break;
}
...
}
this.forceUpdate();
}
...
<TextField
...
onChange={event => this.handleTextChange(event)}
/>
I have several question regarding this approach. First I do not understand why in handleTextChange we can write to dataItem. It does work apparently.
dataItem.carId is set in the example code but I thought
const {dataItem} = this.props;
gives us a local read-only variable dataItem just to read from the props...
Next thing I think is a poor design. After reading in a book about react I think we should not write to props but only set a state.
In my example I get the the dataItem from the redux-state. The mapStateToProps maps it to the (read-only) props of the component, right?!. But I want to EDIT it. So I would have to copy it to the state of my component?
But where to do it?
Once in the state of my component I could simply call this.setState for the various text-fields and the component would render and I could abstain from forceUpdate(), right?!
Can someone explain how the redux status plays together with the component status and props for this example?
In redux or react, you shouldn't write to the props directly because you should keep your props as immutable. Redux forces us to use immutable state because state is a source of truth for the application. If the reference to state changes then only your app should render. If you'll mutate your state (objects) then the references don't get changed and your app doesn't know whether some state has been changed or not. React/Redux doesn't give you read-only objects automatically. You can mutate them anytime but as I told you, it can cause problems that Your app won't know when to re-render. If you want to have this read-only property inherently, you should probably use immutable.js
About your second question that you'll have to copy the props to the component's state and where you should do it. You should do it in the constructor of the component and you should use immutibility helper
import React from React;
import update from 'immutibility-helper';
class Modal extends React.Component {
constructor(props){
this.state = {
dataItem: dataItem,
};
}
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.state.dataItem, text); //fire a redux action to update state in redux
this.setState(update(this.state, {
dataItem: {
carId: {$set: text},
}
});
break;
}
...
}
}
}
You wouldn't have to do forceUpdate in such case because the reference to state will change and the component will re-render itself.
Also, you can use forceUpdate in your application but personally I don't find it a great idea because when React/Redux is giving you the flow of state, by using forceUpdate, you're breaking the flow.
The last question is how redux and react state plays together. That is also a matter of choice. If I have a app level state, e.g., in your case you've some app level data, you should put that in your redux state and if you have a component level things, such as opening a modal or opening a third pane. That's the convention I follow but that can really depend on how you want to exploit react and redux state.
Also, in above code, I put the redux state in component state too (because you asked where to put that) but Ideally you should fire a redux action and update in redux state. In this way, you will restrict yourself from state duplication in react and redux.
import React from React;
import {updateItem} from './actions';
class Modal extends React.Component {
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.props.dataItem, text); //fire a redux action to update state in redux
break;
}
...
}
}
}
const mapStateToProps = (state) => ({
dataItem: getDataItem(state), //get Data Item gets Data from redux state
});
export default connect(mapStateToProps, {updateItem: updateItem})(Modal);
in Actions:
updateItem = (dataItem, text) => dispatch => {
dispatch({type: 'UPDATE_ITEM', payLoad: {dataItem, text});
};
in Reducer:
export default (state = {}, action) => {
switch(action){
case 'UPDATE_ITEM': {
return {
...state,
dataItem: {
...action.dataItem,
carId: action.text,
}
};
}
}
}
In this way, your state will be pure and you don't have to worry about immutibility.
EDIT:
As constructor will be called only once, you should probably use componentWillReceiveProps so that whenever you render the component, you get the next updated props of the component. You can check whether the carId of dataItem is same or not and then update the state.
componentWillReceiveProps(nextProps){
if(nextProps.dataItem.carId !== this.props.dataItem.carId){
this.setState({dataItem: nextProps.dataItem});
}
}
You should only use redux when you want different, unrelated components in your app to know and share the specific state.
e.g. - When a user logs in to your app, you might want all components to know that user so you'll connect your different containers to the user reducer and then propagate the user to the components.
Sounds like in this case you have a classic use case for using the inner state.
You can use the parent of all TextFields to maintain all rows, edit them by index, etc.
Once you start using redux, it's really easy to make the mistake of transferring the entire state of the components to the reducers, I've been there and stopped doing it a while ago :)

react-redux can update child smart components without updating parents?

Imagine the following React structure:
SmartComponentA -> DumbComponentB -> SmartComponentC
Also imagine that SmartComponentA and SmartComponentC each get connected to different slices of the state in their mapStateToProps functions.
Lastly, imagine that we put a console.log in the render method of each of these components.
When I actually try this, on the first render, I see that all components log as expected. But then if I change the data for SmartComponentC, I only see a single log message (C's log message), and I don't see SmartComponentA or DumbComponentB logging anything. How is that possible? How is react-redux getting React to update a child without updating its parents?
I would have assumed that the overriding of shouldComponentUpdate inside of the connect method would mean SmartComponentA would not get re-rendered (since its slice of the state didn't change), and therefore would cause a short-circuiting that would prevent SmartComponentC from getting re-rendered. While connect's implementation is not the same as the pure render mixin, both work by changing shouldComponentUpdate, but the pure render docs clearly state that React will "bail out" (as they put it) if a parent doesn't need to re-render:
for C2's subtree and C7, it didn't even have to compute the virtual DOM as we bailed out on shouldComponentUpdate.
source
If my question still isn't clear, here is sort of pseudo-code for the setup, and I'm asking why I can keep typing in C's input and it only log's C's messages to the console and not A's and B's (why is it not short-circuiting)?
//////////////////////////////////////////////
const SmartComponentA = (props) => {
console.log('rendering SmartComponentA');
return <DumbComponentB bData={props.bData} />;
};
const mapStateToProps = (state) => { bData: state.bData };
export default connect(mapStateToProps)(SmartComponentA);
//////////////////////////////////////////////
const DumbComponentB = (props) => {
console.log('rendering DumbComponentB');
return (
<div>
{props.bData}
<SmartComponentC />
</div>
);
}
export default DumbComponentB;
//////////////////////////////////////////////
const SmartComponentC = (props) => {
console.log('rendering SmartComponentC');
return (
<div>
<input value={props.cValue} onChange={props.changeCValue} />
</div>
);
}
const mapStateToProps = (state) => { cValue: state.cValue };
export default connect(mapStateToProps, { changeCValue })(SmartComponentC);
//////////////////////////////////////////////
On the first render I see all log messages, then if I keep typing in the input, I only see C's log message each time I press a key.
Prop changes trigger the React component lifecycle which will typically trigger the lifecycle of each child component unless -- as you observe, the process can be short-circuited by shouldComponentUpdate.
But prop changes aren't they only thing that triggers the component lifecycle -- state changes do too. And that's how connect function works. The Connect component subscribes to the store and on any store change checks to see if it will update the smart component's props (based on mapStateToProps). If so it will set it's own state triggering the lifecycle functions for the Connect component and it's child.

Resources