Tabs2 re-renders on state change, effectively rendering the renderActiveTabPanelOnly prop useless - reactjs

Not sure what I'm missing here: looking at the example source code in both uncontrolled mode and controlled mode call a setState method on tab change (https://github.com/palantir/blueprint/blob/master/packages/core/examples/tabs2Example.tsx). However, in my environment, this call of setState in my onChange listener is re-rendering the component containing my Tabs2 element meaning that only 1 of my tab panels (specifically the one I'm looking at) are preserved.

I think you may be misunderstanding the purpose of the renderActiveTabPanelOnly prop:
when true, only the active tab is mounted in the DOM (the other non-active tabs are removed).
when false, all tabs are mounted in the DOM but only the active tab is visible (the non-active tabs are hidden via CSS).
Does that clarify the usage?

Related

React & Canvas: changing state is breaking Canvas functions

I am trying to use a parent component to control animations in a child Canvas element. Specifically I want an animation to happen when a user inputs a correct answer.
It works until the user changes the state in the parent component, and then it no longer works.
I've made a stripped-back, minimal version of my code here to show my issue: https://codesandbox.io/s/epic-leaf-08jqvy?file=/src/App.js
My desired behaviour is that the red box bounces when a user clicks submit. That happens if they don't type anything in the input box, but as soon as you enter anything into there - changing state and re-rendering the component - the button no longer triggers the animation in the Canvas child component.
As far as I can tell, the issue is to do with changing the state when inputing text. If I make a version where the input is just assigned to a variable, it works fine, but I need to be able to use state and re-render other parts of it.
I have put a console.log in the jump() function, so I can see that it is being called, but no animation is taking place in the canvas.
I assume that what's happening is that everything is being re-rendered when the state changes, and so the useRef is no longer tracking to the right thing.
Things I've tried:
putting the canvas in a memoized component to prevent it from re-rendering
using eventlisteners to see if I can trigger the animations in other ways - keydown ones work, but I need the user to be able to type, so I tried other ones (like hashchange or audio.play) but neither of those worked.
You can see the thing I'm actually trying to build here: https://papaya-platypus-86565f.netlify.app/play Basically users answer questions and an animation plays depending on whether they're right or wrong, to give it a game-y feel.
Thanks!
I like your red box as well as your reasoning. Yes, the input state changing on keystroke is causing the entire App component to re-render. Note that your App.js component has a lot going on (all good stuff), such as your Box class instantiation, your canvas instantiation, etc.
The key is to break your components into smaller parts, particularly separating stateful components from non-stateful components. We don't want your canvas re-mounting on every input change, so we make them sibling components!
Here's a working example of your code, just in smaller components:
https://codesandbox.io/s/strange-julien-d3n4zm
I hope this helps.

Hiding Components in order to keep their state in React

I have a component with children. I would like these to be shown or not depending on the press of a button. I can do this by creating a state variable childrenVisible, and then rendering or not depending on it.
But I have noticed that if I have state on a child Component it will get reset when toggling childrenVisible. I think this is because components gets destroyed when not rendered on a re-render.
I would like to keep the elements around, so I can keep their state, but just hide them depending on childrenVisible. Is this possible in React?

React check if component is in focus from a TabView

I have a TabView component that has multiple tabs containing components. These components have entire hierarchies of other components. How could I know from any child component nested arbitrarily deep in one of these hierarchies whether or not it's parent tab is in focus in the TabView?
Preferably, I would want to implement this similar to react-navigation's withNavigationFocus (or an equivalent hook) so that any component can know if it's in tab focus without having to pass props down the chain of components. I'm thinking you could have a TabViewContext that components can register themselves as focus listeners to by doing a useContext. The TabViewContext would be provided by the TabView and the TabView would be responsible for determining what registered listeners are in focus when tabs change. My dilemma is I don't know how the TabView could determine efficiently what nested child components come into focus when the tab changes. Any ideas?
In case the other parent tabs are hidden, you could test for visibility in plain JS, rather than have a much more complex solution...
Checkout this answer on how to do this.
So components that care about the visibility of their parent tab could use a ref for their own DOM elements and test whether they're visible or not. You could build this into a simple helper function or a hook
EDIT:
I'd suggest going with something like this:
Each Tab will provide a context with method for any descendant to register a callback that will be called when the Tab is hidden. The TabView can pass a "isVisible" prop to each tab (if it doesn't already), so Tab can know when its display changes.
When a Tab changes from visible to hidden. All registered callbacks will be called.
I would of course write a hook or a helper function to create this TabVisibilty context so each Tab component can use it in a reusable manner.

Prevent React hook from being called when state changes

I noticed that when you use useState in a hook and then use setState to change the value that is being cached, the hook will be called again and re-render the component. While this may be desired most of the time, I have one case where I don't want to re-render when the state changes. This case is when you have a navigation menu (tabs) at the top of the page and when you click on a tab, it shows content in a pane beneath it. I really only want to hide the content for the tab that is currently shown and then display the content for the tab that is selected. When content is hidden, this is essentially setting the css "display" style to "none". This is desirable to preserve the state of the content's pane and also avoid effects like retrieving data.
I can think of one solution to handle this but it does require splitting the components into isolated modules. I am curious though whether there is a way to change state but without having the side effect of a component being re-rendered.

Conditional class rendering based on state for mapped items

https://codesandbox.io/s/vjonpw8xzl
(not responsive, view in new window/fullpage to see correctly)
desired result: when clicking the down(expand)icon on each card, they operate independently of each other, opening/closing the expanded menu className: expanded by toggling on the className: 'show' and className: 'hide'
whats happening: the state in app is getting updated, when the expand icon is clicked, and all of the expanded menus are opening in unison.
i understand why this is happening, i just don't know how to fix it.
if it were my own data, i'd add this to each object in state
isExpanded:false
and toggle it, but since a load of data is coming in from an API i don't know how to do this. any help is appreciated.
Instead of handling your state logic for isExpanded in the App component you can move it to the Character component and remove all isExpanded / toggleExpanded props.
You need to decide that which states are actually defining the component and are needed for it. As isExpanded & toggleExpanded are something specific to character component they should reside in that component.

Resources