My React component does not update in the Safari browser - javascript

Thought everything was going great. Works perfectly in Chrome, FF, Edge, even IE 11!
The parent component holds all the sate. I pass the bets object to the child component which calculates a count to pass to the grandchild component to display.
The parent state 'bets' is an object with the keys as an ID and the value as an object.
The parent state is correctly changing when I interact with the app. Why will only Safari not update when the parent state changes? (on iOS and MacOS)
Parent
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
bets: {}
};
}
}
Child
getBadgeCount = (league) => {
const bets = this.props.bets;
let count = 0;
Object.keys(bets).map((bet) => bets[bet].event.league === league && count++);
return count;
};
// ...
<ChildItem count={this.getBadgeCount(league)} />
GrandChild
class GrandChildComponent extends React.Component {
render() {
const { count } = this.props;
return (
<div>
<div>
{count > 0 && <div>{count}</div>}
</div>
</div>
);
}
}
I console.log the count inside the grandchild render and in the componentDidUpdate and it shows the right number. Is there something apple/safari specific with react I am missing?

In the case where there are variables being changed based on which you can switch between the styles (opacity: 1 or opacity: 0.99) You can try adding a key to the element which is not being updated in Safari/iOS.
<div key={new Date()} className={'myComponent'}>{Count}</div>
I ran into the same problem, this seems to work for now.

So I did solve the very bizarre issue after a few days.
What was happening was I guess the engine in the Safari browser was not re-rendering the little badge icon correctly. I would print out the value inside the DOM, OUTSIDE of the styled badge I was using, and the number would update as expected... Leading me to believe the issue was related to styles.
THEN after removing what I thought was the CSS causing the issue, I noticed that it looked like safari was 'partially' updating the value in the badge. It appears to have half re-rendered, and the previous number collides with the new value... Not sure if this is some obscure problem with the render engine?
After adding and removing all the CSS one by one, the issue remained so I decided to trick the browser to 'force' render with a simple calculation inside the grandchild where it was being rendered:
const safariRenderHack = { opacity: count % 2 ? 1 : 0.99 };
<div style={safariRenderHack}>{count}</div>
Not a great solution but oh well I guess it's fixed. ha

I just run into similar problem.
Safari didn't re-render some texts implemented as:
<span>{someValue}</span>
On 'someValue' field update, reactJS worked fine (element was requested to render) but Safari re-renders only area of new value (shorter than previous). UI glitches :-/
If I done anything to CSS via Developer tools, element has been rendered again and looks fine :-/
After some tries, I luckily used a 'display: block;' style property and it starts to re-rendering absolutely fine. Also 'display: inline-block;' will fix that problem too, if it is needed to be used.

I had a similar problem where a center positioned span using flexbox inside a div wasn't updating correctly in Safari.
Restyling this div to use display:block solved this rendering issue so this might be worth looking into if anyone runs into the same problem in the future.
P.S. Looking at the styling code now, making the change was actually a much cleaner solution as it reduced the amount of lines from 4 to 1 but this might not be the case for all scenarios of course.
before:
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
after: (is a div so display:block is already used)
text-align: center;

When using map function , React looks at the keys :
To identify which items have changed, are added, or are removed.
Solution :
Now, As you use map function u must Add Key to the div inside map .
Use a key variable that is connected to the logic behind clicking , so that when clicking the key , the variable is updated and so React will rerender this grandchild div .
As the following code :
<div key={Count} className={'myComponent'}>{Count}</div>
PS : I discourage to use Date as a key , because I f u are clicking quickly it glitches , while if u are connecting it with a state with the logic behind clicking , it won't .

this may help you:
will-change: transform;

I know it may be late but what helped me with Safari not re-rendering reused components was:
will-change: opacity;
You may have to experiment which element in your HTML hierarchy to apply this to.
It makes no sense because no opacity was being changed in my case but it did fix the problem. Thank you, Apple...

We also ran into this problem. So, I did a short research about it and got the gist as follows:
It works by providing the unique key to the element(key={new Date()} works bcz unique even with seconds) which is not rerendering properly say a description as text inside it that changes by button or an event.
So what I got as a gist basically, providing the individual key make new dom element to react during reconcilation process rather than changing same value inside the element so basically that element showing object is made unique instead of changing value inside that dom element so I think safari specific it is as it is holding state of the element rendering dynamically. So after providing the key prop you are basically rendering the unique different element(obviously with different text/object inside it). This basically utilizes here the concept of keys while we render a list in react component.

Related

React focus div after it has loaded

I'm currently experimenting with React, and I've now run into an issue that I can't seem to solve.
In my application, I use a React library to handle hotkeys, these hotkeys have a scope, so when I want a certain set of hotkeys to be active in a div, I have to wrap that div with a <HotKeys> tag.
I have to toggle some of these divs, so I'll have something along the lines of
isActive ?
<HotKeys ...>
<div ...>...</div>
</HotKeys>
: <div ...>...</div>
I now need to figure out a way to focus the div when it's created. Pretty much every article on the web suggest something like this:
const focusRef = useRef(null);
useEffect(() => {
focusRef.current.focus()
})
return (
isActive ?
<HotKeys ...>
<div ref={focusRef} tabIndex={-1} ...>...</div>
</HotKeys>
: <div ...>...</div>
)
I've tried some variations, including having the div top level (without the <HotKeys> wrapping them), all to no avail.
When I print the focusRef object in the useEffect method, I do get the expected output, focusRef is correctly set and current is populated, but calling the focus method doesn't work. At one point I tried calling the focus method from a button and manually triggering it after the component had fully loaded, it seemed to work (document.activeElement was being changed), then for some reason it stopped working again. All this leads me to believe that somehow the component hasn't fully loaded, despite calling the useEffect hook, which, if I understand correctly, triggers when the element has rendered for the first time/after every change to state.
I'd really appreciate some help with this, since I basically started learning React yesterday.
You must to use an useCallback , because useRef don't notifies you when ref was created
https://reactjs.org/docs/hooks-reference.html#useref
I think I figured it out.
You were right about using the tabIndex but you needed to pass it in as a string like this:
tabIndex={"-1"}
When you first load it a dotted line box surrounds the div that has the ref attached.
check out this code sandbox:
https://codesandbox.io/s/nifty-wildflower-eqjtk?file=/src/App.js
grabbed from this accepted answer where they pass in a string:
Need to put focus on div in react

Page Jumps on Component Re-Render (ReactJS/Gatsby)

Describe the Problem
I am making a very simple ReactJS/Gatsby website for someone and I am having an issue with one of my functional styled components when it re-renders. The problem is that it is causing the window to jump (scroll) after the re-render is complete.
The re-render is triggered by the user clicking on a span element (enclosed in an li element) which fires a function.
The list of li elements is determined by the state of the component. The overall parent component has a fixed height which is why I am having trouble diagnosing the issue.
What I Expect to Happen
The component to re-render and the window's scroll position to remain where it was when the user initiated it.
What Actually Happens
When the user clicks the element the page appears to jump (scroll). Sometimes it does so and remains in the new position, sometimes it does so and then returns to the original scroll position.
What I've Tried
I've tried following advice from other questions which suggest using event.preventDefault() and others which suggest moving the styling out of the component itself and, instead, opting for using classes.
Neither of these solutions worked.
I have managed to definitively find that the issue is due to setActiveTabs -- which causes the re-render of the ul element -- as logging window.scrollY both prior to it firing and after it completes displays a different value.
Edit 2:
I have managed to figure out that the issue is with making the list items targetable. It seems that either adding the tabIndex="0" attribute or making the li child an interactive element causes this bug.
Does anyone know a way around this?
Edit
The full frontend source code can be found in the following GitHub repo: https://github.com/MakingStuffs/resinfusion
In order to solve the issue I needed to prevent the clicked element from being targeted on the re-render. In order to do this I edited the clickHandler so that it uses element.blur() after setting the state.
The click handler is as follows:
const forwardClickHandler = event => {
setLoading(true)
const clickedSlug =
event.target.closest("button") !== null
? event.target.closest("button").getAttribute("data-slug")
: event.target.children[0].getAttribute("data-slug")
const categoryObject = getNeedle(clickedSlug, categories, "slug")
const subCatObject = getNeedle(clickedSlug, subCategories, "slug")
const serviceObject = getNeedle(clickedSlug, services, "slug")
const associatedChildren = getAssociatedChildren(
categoryObject
? categoryObject
: subCatObject
? subCatObject
: serviceObject
)
setBgImage(associatedChildren[0].thumb.localFile.childImageSharp.fluid)
setActiveTabs(associatedChildren)
event.target.blur()
return setTimeout(() => setLoading(false), 1000)
}

Why are the state/css transition times not identical for this simple slider made with ReactJS?

I made a functional demo sandbox here
This is a basic array cycler with 3 elements. And these 3 elements are rendered as slides which move visually left/right depending on the direction you pick.
I don't think the approach I took to make this work is a good one, and if you have suggestions on that I'm open to it.
But the actual question, in order to make the sliding work "equivalently" in both direction i.e. left/right I had to delay the one "sliding" to the left. My naming convention is kind of confusing too because you click the button e.g. prev/next and the array cycling/sliding is flipped. I did use an anti-pattern with the external variable that holds the direction selected since I was having problems with multiple states affecting the slider/causing rendering issues.
But TL:DR this is the onClick handler for the prev/next buttons passing in boolean for direction.I'm using a CSS animation for the motion part. I'm also aware nested ternaries are bad.
const cycleArr = cyclePrev => {
if (!slideDone) {
return;
}
setSlideDone(false);
const newArrSort = cyclePrev ? cycleLeft(slides) : cycleRight(slides);
slideClassRef.current.classList = slideDir
? `App ${slideDir === "left" ? "slide-left" : "slide-right"}`
: "App";
if (slideDir === "left") {
setSlides(newArrSort);
setTimeout(() => {
slideClassRef.current.classList = "App";
setSlideDone(true);
}, 1050);
} else {
setTimeout(() => {
slideClassRef.current.classList = "App";
setSlides(newArrSort);
setSlideDone(true);
}, 1000);
}
};
I'm aware I could have just used something off the shelf eg. slick carousel but this is a good demo of my current problems with state planning in ReactJS. I'm trying to get better/think better at it.
I'm not sure I quite understand your question, if you are referring to the additional 50ms delay when sliding, my best guess is that the call to setSlides(newArrSort); sets the state and also triggers an immediate React re-render of the component. This probably takes some amount of time, hence the desynchronisation with the CSS transition.
Anti-patterns are not there to make your life difficult, they are there to stop you getting into a confusing mess :)
Components can re-render whenever React deems it necessary, which is why state should be properly stored, and pretty effects done in useEffect hooks. I would recommend a more React-based data flow, where you update the state at the top, and let it propagate downwards, reacting to the new state, applying the correct CSS transformations. It's declarative, like HTML and CSS. You don't tell the browser what to paint, you describe how to paint it.

Animate movement of React component from one to another

I'm trying to figure out how to animate moving react component from one to another. For example take very simple, yet interesting card game: you may place any card to a deck, or take top card from the deck.
To make it work, I have 4 classes - <Board> which holds two <Card Collection>: "Deck" and "Hand" components. In constructor, they generate CardModel items and render them via <Card> component. <CardCollection> component has special onCardClick prop which takes callback function. In my case it's onCardClick={/*this is equal to <Board>*/ this.transferCard("Hand")}
Board.transferCard takes clicked CardModel from state of one component and pushes it to another.
The problem is animation - I want card to fly, preferably through center (optional!) from old place to new. I am able to place the newly created Card in "new place" to the same place as old component, but, since i jsut strated to learn React, I'm not sure where exactly I should start. Wrap in ReactCSSTransitionGroup? pass "animate: from{x,y} to{x,y}" property to <CardCollection>?
So the full question is what is the most generic, controllable and "react" way to animate this situation?
JSFiddle base question version: https://jsfiddle.net/fen1kz/6qxpzmm6/
JSFiddle first animation prototype version: https://jsfiddle.net/fen1kz/6qxpzmm6/1
I don't like <this.state.animations.map... here. Also the Animation component looks like an abomination for me, I'm not sure this is the good architecture style for React.
The main mistake I did is try to mix render function with animation. No! Render should not contain anything related to animation (preferably not even starter values), while all the animation should happen after render. The only thing that bothers me is that i still should have state of animations in CardCollection just to throw it into creation of Card
Working example: https://jsfiddle.net/fen1kz/6qxpzmm6/4/
let animation;
if (this.animations[cardModel.id] != void 0) {
animation = this.animations[cardModel.id];
this.animations[cardModel.id] = void 0;
}
...
return <Card
cardModel={cardModel}
onCardClick={onCardClick}
animation={animation}
position={this.getCardPosition(i)}
index={i}
key={cardModel.id}
ref={cardModel.id} // prolly not needed
/>
You can try to use a package I made called react-singular-component, which might actually do what you need.
The component allows you to render a component server times and by a given priority only the highest one will render. Mounting and uncounting the given component will cause the next highest priority to render instead with an animation moving and wrapping the component from its last place and size to the new one.
Here is the Github page: https://github.com/dor6/SingularComponent
Just an idea: You could try to use jQuery animate for animation of moving some HTML element from one place to another. Once animation is complete there is a complete function property only then you could trigger your onCardClick.

Polymer change custom element

I have a question regarding custom elements in polymer.
I have acustom element that has a style nested inside it's template, for a div inside the template like so:
<template>
<style>
#navWrapper{
height: auto;;
color:red;
position:fixed;
background-color:black;
width: 100%;
}
</style>
<div id="navWrapper"><content></content></div>
</template>
Now, I'd like to change the color of navWrapper when I scroll down the page.
I want to use jquery for that.
Therefore I have a script inside of my custom element like so:
<script>
Polymer('responsive-nav',{ready: function() {
$(window).scroll (function () {
var sT = $(this).scrollTop();
if (sT >= 100) {
$(this.$.navWrapper).css({backgroundColor : 'rgba(0,0,0,0.0)' })
}else {
...
}
})
} });
</script>
Now sadly that does nothing. I have put an alert(test) before the $(this.$.navWrapper) to find out if I even get to that point and I do. Also when I want to change the background color or something else from an element that lives in my normal HTML file it works. For example $("#testDiv").css({...}) changes. So my question is: How do I access the css of my element properly?
Looks like your JQuery CSS call is wrong.
I think it should be:
.css("background-color", "rgba(0,0,0,0.0)")
rather than
.css({backgroundColor : 'rgba(0,0,0,0.0)' })
Cross referencing what you've done with the JQuery docs, you've definately missed the '-' out of 'backgroundColor', but also I don't see anything in the docs that states using a JSON object to make the change.
You can however use an array of property names and values (Which is what I suspect you may have been trying to do)
Update (Approx 1 hour later)
Since Iv'e been wrestling with a not too dissimilar problem today (but involving bootstrap rather than jquery) I was already investigating things around similar concepts.
As a result, I took the OP's original code and started playing with it.
What I present below is a partial JQuery solution (we still use JQ's scroll detection) where I also figured out an alternate way of changing the style using polymers conditional binding syntax.
http://www.polymer-project.org/docs/polymer/expressions.html
Essentially what i did was to pass a user data object into the scroll event at set-up time.
This user object contained a property that reflects the current polymer object (This is needed so that the JQ handler when it fires can update polymer properties)
When the window scroll event occurs, the handler extracts this property, then uses it to get at a local variable inside the polymer element, and thus updating it with the current scroll top value.
Since that locally scoped property is part of the actual polymer object, ANY polymer data-binding can read it, at this point it's simply just a matter of creating a couple of styles, then using the expression binding to pick the correct style.
Remember, styles cascade, so you can easily just make one master style for the whole thing, then 2 styles that simply just change the background color as appropriate.
Expression bindings work by using the text on the left side of the : only if the expression on the right evaluates to true eg:
{{ {frogs: a == 1} }}
would replace the expression binding with '' if property 'a' was NOT equal to 1 and set it to 'frogs' if property 'a' was equal to 1.
Expression bindings however are singular in nature, so in order to have more than 1 expression binding you need to pass the entire thing through one of polymers filters, specifically the 'tokenList' filter.
once you do this, you can build a whole object of different checks up, so expanding on our previous example:
{{ {frogs: a == 1, tadpoles: a == 2} | tokenList }}
will now make the result be equal to '' if property 'a' was NOT equal to 1 and NOT equal to 2 , while setting the result to 'frogs' if property 'a' was equal to 1 and setting the result to 'tadpoles' if property 'a' was equal to 2.
you can expand this to as many checks as you like, but the more you add in (I'm guessing anyway - iv'e not tested it) the slower performance is likely to be.
For now, the code below will do exactly what you need, well once you make a few alterations so that it targets your own elements and set's your own styles up anyway :-)
<link rel="import" href="polymer.html">
<polymer-element name="x-mypolymercomponent">
<template>
<style>
.under100
{
background-color: green;
}
.over100
{
background-color: red;
}
</style>
<h1 class="{{ {under100: scroll_top <= 100, over100: scroll_top > 100} | tokenList }}">This header has a dynamic class</h1>
</template>
<script>
Polymer('x-mypolymercomponent', {
ready: function ()
{
$(window).scroll({scope: this}, function(event) {
var sT = $(this).scrollTop();
event.data.scope.scroll_top = sT;
})
},
scroll_top: 0,
});
</script>
</polymer-element>
Iv'e just tested this in the .NET project I'm currently working on, and it works fine in chrome 36, there are still problems in Firefox though, but this I think is possibly due to a bug I discovered last night so I wouldn't worry too much about it.
I have JQuery loaded at page level too, so it would appear that the component can pick up the page events just fine from it.
Give it a try see where you go from here, I'm about to adapt it for my specific issue, then turn it into a typescript module :-)

Categories

Resources