I am working on a react component to copy text on clipboard. I am using document.execCommand('copy') for it, which is working fine for browsers. However it may not found "document" object in other environment and will break there ie. node.
Is there any alternative I can use to make it work for cross platform?
jsdom is widely used in Node.js applications to provide support for some client-side features, primarily DOM. document.execCommand is not among them.
In order for document.execCommand('copy') to not cause an error during SSR, client-side features in use can be stubbed in Node:
global.document = {
execCommand() {}
};
An alternative approach is to detect Node.js environment, e.g. with detect-node. Either with in-place conditions:
if (isNode)
document.execCommand('copy');
Or by using loosely coupled components and IoC/DI containers. Redux store or React context can act as a container which platform-dependent components can be read from.
For instance, with React 16.3 context API:
const ClipboardComponent = (props) => /* default implementation */;
export const PlatformContainer = React.createContext({
ClipboardComponent,
});
The component is retrieved from the context where it's used:
<PlatformContainer.Consumer>{({ ClipboardComponent }) =>
<ClipboardComponent/>
}</PlatformContainer.Consumer>
It's rendered with default implementation on client side:
render(<App />, rootElement);
And no-op implementation can be provided in entry point on server side:
renderToString(
<PlatformContext.Provider value={ { ClipboardComponent: () => null } }>
<App />
</PlatformContext.Provider>
);
Related
I am working on this QR code based application which is using a ClearDB MySQL database that is stored on Heroku servers. The frontend communicates with the database by using a REST API built with Node.js and Express
Whenever a user scans a QR code, the value of the code changes in the database. But I don't know how to reflect that change instantly in the frontend. Basically what I need help with is finding a way to automatically refresh the page whenever that value changes in the database and display the new QR code based on the new value. Such that when a user scans the code, it instantly updates on his page, not only in the database.
I looked into sockets but didn't quite understand how to integrate it into my application, especially when it comes to the frontend.
You can use a state value for detecting the change.
You declare a state value of which type is boolean(any type you want). Then you can implement setState function(in case of class component) or useState hook(in case of functional component) in the module of updating data in the database.
For example:
const [isChanged, setChanged] = useState(false);
const updateQRData = (data) => {
... Update codes
setChanged(isChanged => !isChanged);
}
Then you can use useEffect hook:
useEffect(() => {
... Write some codes
}, [isChanged]);
How are you storing the information for the QR codes? In Redux? Or local state? Are you using class-based components or hooks? If you want the least amount of headaches, I'd heavily suggest hooks. Let me know and I can give more specifics for the code.
Your final product would look something like this:
const [qrData, setQrData] = useState(null)
const updateDatabase = () => {
axios.post(`your/database/url/with/your/data/attached`).then(res => {
setQrData(res.data.your.response.object.from.database)
}
}
I have below folder structure with gatsby react
How can I call a rest full api when using gatsby? I have gone through this but don't know how do I call multiple api at once on different components.
This really depends on what you're trying to achieve and is a very broad question. Firstly, the file structure image above has no bearing whatsoever on the solution. Secondly, don't be hung up about Gatsby. Gatsby is a tool for building React apps, so you're really just building and populating React components, so if you're stuck, search for help using React.
It really depends if you're trying to populate a bunch of components in a page ready for the user to use or whether you need to 'control state' for a page containing lots of components that share data. You can call an API from the component itself, or you can call the API from a 'parent' component and pass the data into the target (known as Child) component.
If I have a component that needs data but the data isn't used for app state, then I'd use the componentDidMount() method of the component itself, which is clearly demonstrated in the link you posted. For example, you want a dropdown/select loaded with a set of default data.
componentDidMount(){
fetch(ApiURL)
.then(result=>result.json())
.then(result=>this.setState({countries}, this.buildSelectOptions(countries)))
}
What this does is when the component has been mounted on the page, the componentDidMount method fires. This then calls the fetch command and sticks the raw results into the countries state property. It then calls the 'this.buildSeletOptions(countries)' method to build the 'options' array for the select component.
E.g.
buildSelectOptions(countries){
var optionsArray[];
var newCountry={key: 1, value:'All', text:'All'};
optionsArray.push(newCountry);
for(var i = 0; i < countries.length; i++) {
let nextCountry = {key: i+2, value:countries[i].name, text:countries[i].name};
optionsArray.push(nextCountry);
}
this.setState({options: optionsArray});
}
To call multiple APIs at once, I would go to the first parent where all affected child components are underneath. E.g. I have a page with multiple components and they all need populating.
class SomePage extends React.Component {
constructor(props) {
super(props);
this.state = {compOneData:'', compTwoData:''};
}
componentDidMount(){
this.loadData();
}
loadData(){
fetch(someApiURL)
.then(result=>result.json())
.then(result=>this.setState({compOneData: result});
fetch(someotherApiURL)
.then(result=>result.json())
.then(result=>this.setState({compTwoData: result});
}
render(){
return (
<div>
<ComponentOne data={this.state.compOneData} />
<ComponentTwo data={this.state.compTwoData} />
</div>
);
}
}
export default SomePage;
What happens here is that when SomePage loads, the componentDidMount method is called. There are now two fetch calls in here and they will be called in parallel. React setState will then handle the data returned by the APIs. Once setState has completed, React notes the change in state and pushes the data to the two child components. This is a trivial example and doesn't explain how to handle situations where the second API call is dependent on data from the first, but should give you enough to go on.
Find it in Composition section
Where it come from?
// This is very useful for server side rendering, because I can wait
// until data is available, then synchronously render the app.
store.dispatch(
makeSandwichesForEverybody()
).then(() =>
response.send(ReactDOMServer.renderToString(<MyApp store={store} />))
);
I believe it comes from Javascript XMLHttpRequest
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/send
I know window doesn't exist in Node.js, but I'm using React and the same code on both client and server. Any method I use to check if window exists nets me:
Uncaught ReferenceError: window is not defined
How do I get around the fact that I can't do window && window.scroll(0, 0)?
Sawtaytoes has got it. I would run whatever code you have in componentDidMount() and surround it with:
if (typeof(window) !== 'undefined') {
// code here
}
If the window object is still not being created by the time React renders the component, you can always run your code a fraction of a second after the component renders (and the window object has definitely been created by then) so the user can't tell the difference.
if (typeof(window) !== 'undefined') {
var timer = setTimeout(function() {
// code here
}, 200);
}
I would advise against putting state in the setTimeout.
This will settle that issue for you:
typeof(window) === 'undefined'
Even if a variable isn't defined, you can use typeof() to check for it.
This kind of code shouldn't even be running on the server, it should be inside some componentDidMount (see doc) hook, which is only invoke client side. This is because it doesn't make sense to scroll the window server side.
However, if you have to reference to window in a part of your code that really runs both client and server, use global instead (which represents the global scope - e.g. window on the client).
This is a little older but for ES6 style react component classes you can use this class decorator I created as a drop in solution for defining components that should only render on the client side. I like it better than dropping window checks in everywhere.
import { clientOnly } from 'client-component';
#clientOnly
class ComponentThatAccessesWindowThatIsNotSafeForServerRendering extends Component {
render() {
const currentLocation = window.location;
return (
<div>{currentLocation}</div>
)
};
}
https://github.com/peterlazzarino/client-component
<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}>
if you need to open new page on top in React JS app, use this code in router.js
Move the window and related code to the mounted() lifecycle hook. This is because mounted() hook is called on the client side only and window is available there.
Let's say I configure redstone as follows
#app.Route("/raw/user/:id", methods: const [app.GET])
getRawUser(int id) => json_about_user_id;
When I run the server and go to /raw/user/10 I get raw json data in a form of a string.
Now I would like to be able to go to, say, /user/10 and get a nice representation of this json I get from /raw/user/10.
Solutions that come to my mind are as follows:
First
create web/user/user.html and web/user/user.dart, configure the latter to run when index.html is accessed
in user.dart monitor query parameters (user.dart?id=10), make appropriate requests and present everything in user.html, i.e.
var uri = Uri.parse( window.location.href );
String id = uri.queryParameters['id'];
new HttpRequest().getString(new Uri.http(SERVER,'/raw/user/${id}').toString() ).then( presentation )
A downside of this solution is that I do not achieve /user/10-like urls at all.
Another way is to additionally configure redstone as follows:
#app.Route("/user/:id", methods: const [app.GET])
getUser(int id) => app.redirect('/person/index.html?id=${id}');
in this case at least urls like "/user/10" are allowed, but this simply does not work.
How would I do that correctly? Example of a web app on redstone's git is, to my mind, cryptic and involved.
I am not sure whether this have to be explained with connection to redstone or dart only, but I cannot find anything related.
I guess you are trying to generate html files in the server with a template engine. Redstone was designed to mainly build services, so it doesn't have a built-in template engine, but you can use any engine available on pub, such as mustache. Although, if you use Polymer, AngularDart or other frameowrk which implements a client-side template system, you don't need to generate html files in the server.
Moreover, if you want to reuse other services, you can just call them directly, for example:
#app.Route("/raw/user/:id")
getRawUser(int id) => json_about_user_id;
#app.Route("/user/:id")
getUser(int id) {
var json = getRawUser();
...
}
Redstone v0.6 (still in alpha) also includes a new foward() function, which you can use to dispatch a request internally, although, the response is received as a shelf.Response object, so you have to read it:
#app.Route("/user/:id")
getUser(int id) async {
var resp = await chain.forward("/raw/user/$id");
var json = await resp.readAsString();
...
}
Edit:
To serve static files, like html files and dart scripts which are executed in the browser, you can use the shelf_static middleware. See here for a complete Redstone + Polymer example (shelf_static is configured in the bin/server.dart file).