putting bracket next to const - react-hooks

I'm new to react, and I have few question about some react expressions.
I was watching the video to learn how to drag and drop components.
The code below works, but I got confused by these things.
Why const variable bindLogoPos became a function inside div? (line 8, line 18)
What does the three dot means in this code? (I've learned that three dots usually mean the rest of array or object. But I think bindLogoPos is not quite related to an array or object)
What does it mean to use {...bindLogoPos()} like this way? Does it means to call function infinitely? Is this expression possible only in react?
import logo from './logo.svg';
import './App.css';
import {useState} from 'react';
import {useDrag} from 'react-use-gesture';
function App() {
const [logoPos, setLogoPos] = useState({x:0, y:0});
const bindLogoPos = useDrag((params)=>{
setLogoPos({
x: params.offset[0],
y: params.offset[1]
});
});
return (
<div className="App">
<header className="App-header">
<div {...bindLogoPos()} style={{
position:'relative',
top: logoPos.y,
left: logoPos.x,
}}>
<img src={logo} className="App-logo" alt="logo" />
</div>
////////more...

First of all, you got to understand how React works.
React renders your component whenever the internal state changes, so bindLogoPos() would be called when the state of your component changes. Therefore, it won't be called infinitely, but only when the state of your component / app changes.
Secondly, that const x = () => {} is actually an assignment statement, and it assigns an anonymous function to a variable called bindLogoPos. Functions are first-class citizens in JavaScript (functional programming!), so that's why it works like that. Also, the reason why "const variable bindLogoPos became a function inside div" is because useDrag actually gets the function itself as an argument then returns a different function.
Next, the three dots mean to put the result of that function as the entire props. So in your case, it would mean to get a return value from bindLogoPos then assign it as props to the HTML div element. Think of it like passing the entire props as the sole function argument instead of destructuring the object then passing it one by one.
I hope this answers your question, and happy coding!

Here useDrag is a custom hook and it seems it is returning a function called bindLogoPos which when you are calling is returning div attributes in form of a json.
so: const res = bindLogoPos(); // res is a json containing div attributes
and then this {...res} is called destructuring.
So that how it injecting all required attributes (like draggable="true", style ...)

Related

Using React Hooks to Show Component Render Time and Date

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

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-bootstrap ModalTrigger doesn't work on React components

Looks like the ModalTrigger doesn't play well when being supplied a React component. Consider the following:
var MyDiv = React.createClass({
render : function() {
return <div>Click me!</div>
}
});
var Content = React.createClass({
render : function() {
return <div>
<ModalTrigger modal={<MyModal/>}>
<MyDiv/>
</ModalTrigger>
<ModalTrigger modal={<MyModal/>}>
<div>No, click me!</div>
</ModalTrigger>
</div>
}
});
React.render(<Content/>, document.getElementById("mydiv"));
When clicking on the first div, nothing happens, but the second div opens the modal as expected.
The DOM looks identical, but when using the React extension for Chrome I can see that there is an additional React component between the first ModalTrigger and the underlying div, named MyDiv.
The reason this is a problem is that ModalTrigger depends on its child element onClick to show the modal. When using a regular div it works as expected, but since the direct child here is a React component, there is no obvious way to make this connection go to the actual div component.
So my question, is this a shortcoming of react-bootstrap that cannot deal with the way React instantiates components, or is this the normal / expected behavior that I should work around somehow?
Thanks!
Edit:
I wanted to post this as a comment but could not format it properly :/
One way of getting around this is to have MyDiv aware that it has a this.props.onClick method, and trigger it explicitly:
var MyDiv = React.createClass({
render : function() {
return <div onClick={this.props.onClick>Click me!</div>
}
});
This creates coupling (or extra lines for PropTypes/getDefaultProps to decouple), which is far from ideal.
Another way is to wrap MyDiv in another anonymous div:
<ModalTrigger modal={<MyModal/>}>
<div><MyDiv/></div>
</ModalTrigger>
Which is much better, but somehow doesn't feel right either. Any suggestions?
It seems that the answer is under the MyDiv's responsibility, at least in how react-bootstrap works. The magic happens when the top-level div in MyDiv is passed the props, like so:
var MyDiv = React.createClass({
render : function() {
return <div {...this.props}>Click me!</div>
}
});
This is both in step with react-bootstrap's inner components' behavior, and allows MyDiv to remain agnostic of whatever is passed through. It's probably better, as Facebook suggests, to use the harmony destructuring syntax (e.g., var {myProp, ...otherProps} = this.props and then using it in <MyDiv {...otherProps}/>), but, well... harmony.

Children dynamic content with reactjs

I'm trying to experiment here. I want to build a component that auto populates some data from an ajax request after mounting. Something like this:
var AjaxComponent = React.createClass({
getInitialState: function() {
return {
data: {}
};
},
render: function() {
return (
<div>
{this.state.data.text}
</div>
);
},
componentDidMount: function() {
makeAjaxResquest(this.props.url).then(function(response){
this.setState({
data: response.body // or something
});
}.bind(this));
}
});
With that example component, I'd use <AjaxComponent url="/url/to/fetch" /> to display the content.
Now, what if I'd like to access different bits of data from children elements? Can I do something like this?
<AjaxComponent url="/url/to/fetch">
<div>
<header>{RESPONSE.title}</header>
<div>
{RESPONSE.text}
</div>
</div>
</AjaxComponent>
No problem if it doesn't render anything before the ajax request ends. The thing is how could I pass the data for children to render, not as props. Is it possible?
I had a similar scenario where I had similar Components that would query data from different APIs. Assuming you know the expected response from a given API, you could do it the same way perhaps.
Essentially make a generic Component where it props functions as an "API" of sorts, then define different types of sub components and their associated render function.
For example:
In widget, you then do something like this, where widgets is just a plain javascript file with a bunch of functions:
componentDidMount: widgets[type].componentDidMount(),
render: widgets[type].render().
In widgets, it would be like this:
var widgets = {
widget1: {
componentDidMount: function () {
//Ajax call..
},
render: function() {
//How should I draw?
}
},
widget2: //Same format, different functions
Then in some parent component you simply go
< Widget type="widget1" \>
or whatever.
There are a couple weird things about this that probably don't sit right with React. First off, you should take state all the way up to the top-level component, so I wouldn't do my ajax calls in componentDidMount...I'd more likely get the data I want for the widgets I want to render at a higher level, then pass that in as a prop too if it won't change until I make another API call (thinking Flux style flow here). Then, just pass in the data as a prop as well and just specify the render functions:
< Widget data={this.state.data[0]} type=widget1 />
The "gotcha" here is that you are making an assumption that whatever is in this data prop will match what you need in the widget type. I would pass in an object, and then validate it all in the render function etc.
That's one way. Not sure if it's valid, I'm sure someone who knows more could pick it apart but it suited my use case and I now have a library of similar components that I can selectively render by passing in data and a type, then looking up the appropriate render function and checking to make sure the data object contains everything I need to render.

react-bootstrap ModalTrigger doesn't hide when the parent element is unmounted

We encountered a strange behavior when using react-bootstrap's ModalTrigger with an array of items, in that it doesn't go away when the parent/owner item is unmounted. We suspect this has to do with React's virtual DOM and the diffing mechanism, and/or our own misuse of the ModalTrigger.
The setup is simple: a Content react component has a state that holds an array of item names. It also has an onClick(name) function that removes that name from the array via setState. In the render, it uses _.map to create a bunch of Item react components.
Each Item component displays its name and a ModalTrigger that holds a button labeled "delete me". Click on the button and it opens the modal; click "OK" in the modal and it executes the callback to the Content remove function.
When deleting the last item it works fine: the final Item component is unmounted, and with it, the ModalTrigger and its corresponding modal.
The problematic behavior we see is when deleting any item other than the last one. The item is removed but the modal stays open, whereas I would naively expect the modal to disappear since the parent ModalTrigger is gone. Not only that, but when clicking "ok" again, the next item on the list is removed, and so on until the modal happens to be associated with the last item, at which point clicking "ok" will finally hide it.
Our collective hunch is that this is caused by the overlayMixin's _overlayTarget being an anonymous element in the document, so that different ModalTriggers don't differentiate between them. Therefore, when a parent unmounts and React looks for the DOM diff, it sees the previous trigger's and says "hey, that could work".
This whole issue can easily be addressed by adding a hide() call in the Item's inner _onClick() function as is commented out in the code, and we finally arrive at my question:
Am I using ModalTrigger correctly, in that expecting it to go away when the parent is unmounting? This is kind of how I expect React to work in general, which means a bug in react-bootstrap.
Or should I be explicitly calling hide() because that's they way this component was designed?
Following is a piece of code that reproduces this.
Thanks!
var DeleteModal = React.createClass({
render:function() {
return (
<ReactBootstrap.Modal onRequestHide = {this.props.onRequestHide} title = "delete this?">
<div className="modal-body">
Are you sure?
</div>
<div className="modal-footer">
<button onClick={this.props.onOkClick}>ok</button>
<button onClick={this.props.onRequestHide}>cancel</button>
</div>
</ReactBootstrap.Modal>
);
}
});
var Item = React.createClass({
_onClick:function() {
//this.refs.theTrigger.hide();
this.props.onClick(this.props.name);
},
render:function() {
return (
<div>
<span>{this.props.name}</span>
<ModalTrigger modal={<DeleteModal onOkClick={this._onClick}/>} ref="theTrigger">
<button>delete me!</button>
</ModalTrigger>
</div>
);
}
});
var Content = React.createClass({
onClick:function(name) {
this.setState({items:_.reject(this.state.items, function(item) {return item === name;})});
},
getInitialState:function() {
return {items : ["first", "secondth", "thirdst"]};
},
render:function() {
return (
<div>
{_.map(this.state.items, function(item, i) {
return (
<Item name={item} onClick={this.onClick} key={i}/>
)}.bind(this)
)}
</div>
);
}
});
React.render(<Content/>, document.getElementById("mydiv"));
Turns out it was a misuse of React's "key" property. We gave the mapped objects integer keys, so when the render was called again, the same initial keys were given, which is why React thought it should reuse the same DOM element.
If instead we give it key={item} (where item is a simple string) it solves it in our case; however, this introduces a subtle bug whereby if there are 2 identical strings, React will display only one.
Trying to outsmart it by giving it key={item + i} introduces an even subtler bug, where duplicate items are displayed but are delete en mass, but in this case the bug is in the onClick method which would need to be modified to accept an index of some sort.
Therefore my take-away is that the keys must be a unique string, and callbacks should take these keys into consideration when performing any modifications.

Resources