How to tell if Gatsby Image component has loaded? - reactjs

I have a GatsbyImage / StaticImage component and I use the onLoad event to trigger an animation. But once the browser has cached the image, the onLoad event never fires again. So if you visit a different page and then return, the animation isn't triggered.
The usual workaround with an ordinary image would be to attach a ref and then check ref.current.complete to see if the image had loaded. But GatsbyImage refuses to take a ref.
So
Is there any way to add a ref to the GatsbyImage component?
Is there any other way to detect if the image has loaded?
Is the only alternative to use an ordinary img tag (and lose out on all the gatsby image magic)?

Is there any way to add a ref to the GatsbyImage component?
Apparently, you can't. According to this GitHub thread it throws the following exception:
Function components cannot be given refs. Attempts to access this ref
will fail. Did you mean to use React.forwardRef()?
That said, you can try using forwardRef() which will give you a valid scope for your use-case.
Is there any other way to detect if the image has loaded?
Not directly. However, you can try the onStartLoad callback which expose the wasCached boolean that gives your more information and control about the browser cache. You can play around with that boolean to trigger the animation again or to use the value globally along with a useState for example:
<GatsbyImage
fadeIn={false}
className="customImg"
onLoad={() => {
// do loading stuff
}}
onStartLoad={({ wasCached }) => {
// do stuff on start of loading
// optionally with the wasCached boolean parameter
}}
onError={(error) => {
// do error stuff
}}
/>
Is the only alternative to use an ordinary img tag (and lose out on all the gatsby image magic)?
You can find a bunch of dependencies to lazy-load, fade, blur, get background primary color, etc of an image but its implementation, behavior, and compatibility will rely on the library itself. So, yes, there are alternatives but I'm not sure if they are worth it.

Related

React Konva custom context/canvas (for use with canvas2svg)

We're using react-konva, which renders a <Stage> component that manages an underlying canvas/context.
We want to use canvas2svg with it, which is basically a wrapper around a canvas context that tracks all draws to the canvas and maintains an svg representation for when you're ready to access it. It doesn't modify the canvas api, so in theory, Konva would be unaffected in that its canvas draw calls would be the same - they'd just need to be on the context generated by canvas2svg, rather than the one konva automatically generates.
We're looking for something like this, but it doesn't seem to exist. Are there any ways we could use a ref or otherwise hack Konva into using a C2S context? Or maybe we're missing a built-in property.
var c2sContext = new C2S(500,500);
<Stage context={c2sContext}>
canvas2svg doesn't actually work alongside a normal rendered canvas (it's an alternative to one - for example, the arc() method just renders to svg, not svg + canvas), so you need to replace the render context with a c2s instance, call render so the methods like arc() get called, and then set it back.
You can use a ref on the konva Layer and run code like the following on a button press:
setTimeout(() => {
var oldContext = this.layerRef.canvas.context._context;
var c2s = this.layerRef.canvas.context._context = C2S({...this.state.containerSize, ctx: oldContext});
this.forceUpdate();
setTimeout(() => {
console.log(c2s.getSerializedSvg());
this.layerRef.canvas.context._context = oldContext;
this.forceUpdate();
})
}, 5000);
Two possible solutions that skirt around the need for canvas2svg:
Generating an SVG string manually without rendering svg elements
Rendering svg elements rather than canvas elements, and converting the dom to an html string
Obviously #2 has performance issues and #1 requires messier code - and #2 still requires some additional code/messiness, but both are good options to be aware of.

React don't mount component until needed but don't unmount afterwards in list of components

Say I am building an instant messaging with app with React (I'm not doing that exactly, but this is easier to explain). I have a sidebar with a list of conversations and, when you click one, it is shown on the right (similar to this). I don't want to mount each conversation component until the user clicks it, but I don't want to unmount it, just hide it, when they click on another conversation. How can I do this cleanly? There will never be more than about 30 chats for any user.
You can store the enabled conversations in an array that you use to show, and when you disable a conversation you can just add a hidden prop to it which you pass to the conversation and make it return null. This will make it not render anything but will not unmount it since you have not removed it from the array that handles the display of conversations.
example at: https://codesandbox.io/s/wispy-forest-59bqj
This is a bit hard to answer since you haven't posted the code.
But, theoretically, the best way to approach this problem is to transfer the data from your sidebar component and load it onto the right component on a per-user basis. You don't have to mount each "conversation component".
You can do this by with the boolean hidden property in your markup. React will render as usual and simply pass it along to the html, the browser will then simply not paint it.
const HideMe = ({ isHidden }) => (
<div hidden={isHidden}>
can you see me?
</div>
)
I made an example for you:
https://codesandbox.io/s/elastic-curie-t4ill?file=/src/App.js
reference: https://www.w3schools.com/tags/att_hidden.asp

What does "animated" do in react-spring?

I am currently getting to grips with the react-spring animation library.
In some of the CodeSandbox demos (e.g. https://codesandbox.io/embed/j150ykxrv) in the documentation, something is imported called "animated":
import { Transition, animated } from 'react-spring'
and then used like so:
{this.state.items.map(item => styles => <animated.li style={{ ...defaultStyles, ...styles }}>{item}</animated.li>)}
In other examples this isn't used:
import { Spring } from 'react-spring'
<Spring
from={{ opacity: 0 }}
to={{ opacity: 1 }}>
{props => <div style={props}>✌️</div>}
</Spring>
I can't find any mention in the documentation of what this does or why it is used, as it seems you can animate by just passing animated style props into a component.
Are the uses in the documentation part of a legacy version?
Native is optional, if you set it (and then you need the component to extend from animated.xxx) it won't render out the animation like normally react animation libs would do, in other words they call forceUpdate or setState on every frame, which is expensive. With native it will render the component once, then animate in the background using a requestAnimationFrame-loop which sets the styles directly. The values you pass to your target div (or whatever) are not numeric values but classes that receive update events, this is why you need to extend.
Rendering through react is not obsolete, though, it is the only way you can animate React component props. If you have a foreign control, or a D3 graph for instance, you would simply blow props into it while the spring renders them out.
Looking further into the docs, I can see it is used for "native" rendering.
This allows react-spring to bypass React for frame updates. The benefits of this method are improved performance.
It is recommended to use this approach
"Try doing this in all situations where you can"
Just be aware of the following conditions:
native only animates styles, attributes and children (as textContent)
The values you receive are opaque objects, not regular values
Receiving elements must be animated.[elementName], for instance div becomes animated.div
If you use styled-components or custom componemts do: animated(component)
If you need to interpolate styles use interpolate
Summarised benefits:
Your application will be faster, the difference really can be night
and day
You get quite powerful interpolation and keyframing tools (see
below)
You get to animate scrollTop and scrollLeft out of the box (which
React can't normally handle)
Looks like it is used for doing native rendering,take a look a the Transition component , it has a native prop

React-navigation: detect new screen is fully focused

With current version of react-navigation, there are two ways to check if a screen is focused or not, by either (1) calling function isFocused of screen's prop navigation, or (2) hook the component to withNavigationFocused and retrieve prop isFocused.
However, both methods always return true when navigation starts. In my case, I need something triggering only when screen transition ends, i.e. new screen is fully focused. This is to deal with heavy-rendered children, such as camera or map, which should be rendered after screen transition to avoid slow transition animation.
Any idea how to achieve that?
You can try subscribing to the navigation lifecycle events, as described in the docs:
const didFocusSubscription = this.props.navigation.addListener(
'didFocus',
payload => {
console.debug('didFocus', payload);
}
);
Another example usage in this repo

Download SVGs rendered by React

I would like to implement an export function on my SPA, consisting on generating a bunch of SVGs (generated using React JSX) and downloading them one at once.
The number of files being huge, I can't display them at the same time on the browser. I have found React Download SVG which permits to download a SVG which is already inthe DOM.
However, the render pipeline of React does not enable me to batch the downloading because I don't control the display cycle of my JSX SVG.
How could I download all my SVGs (zipping them in a file would be an advantage) without displaying them ?
Thanks in advance,
I worked with similiar problem - generate parametrized SVG paths for CNC purpose.
Problem wasn't with download as invoked manually (after DOM update) - batch download (zipped) planned, also.
Problem was: how to display SVG source/xml in another node/component for debugging - updated on every parameter change.
However, the render pipeline of React does not enable me to batch the downloading because I don't control the display cycle of my JSX SVG.
This is true ... is nome sense, even harder while React Fiber can delay some DOM updates - but we have some possibilities to be notified.
componentDidUpdate() - but 'not called for the initial render'
ref callback - but '... defined as an inline function, it will get called twice during updates, first with null and then again with the DOM element'
I prefer 2nd method for a few reasons, f.e. neutral to SVG (functional) component internals - however it's not guaranteed to be called in every use case - it won't be if not needed - OTOH CDM and CDU are guaranteed to have updated/proper refs when called.
Another hint found somewhere (SO?): use setTimeout to be safe/sure callback called after DOM update.
downloadableReference = el => {
console.log("DWNLD_REF ",el);
this.svgElement = el // save in wrapper, prevent old ref usage
if( !!el ) { // not null - second pass, fresh, updated
console.log("DWNLD_REF ready ", el.outerHTML )
setTimeout( ()=>{
this.notifyDownloadableDependants() // safest way - will be called after CDU
}, 0 )
}
}
This can be combined to batch the downloading. For UX I would render them sequentially - with progress visualisation, cancellable processing etc.
Another possibility: - maybe (not tested) use of react-jsx-parser be helpfull?

Resources