Skip to content

Conversation

@WVAviator
Copy link
Contributor

@WVAviator WVAviator commented Mar 17, 2022

Motivation

Fixes #3390

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Jest Unit Test
  • Checked with example app

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Additional context

Switching to the use of React.Children.toArray() instead of React.Children.map() effectively ignores falsey children rather than throwing an error. However, assuming that a developer may want the ability to enable/disable tabs while the component is mounted created a couple other issues to solve:

  1. The "length" value used in the onPanResponderRelease function continues to reference the old length value, which enables the user to navigate past the last TabView.Item if an item was removed, or prevents them from navigating to the last TabView.Item if an item is added. This was fixed by making the onPanResponderRelease function a useCallback function with a dependency on length, and switching the panResponder object from useRef to useMemo with a dependency on onPanResponderRelease
  2. If the user were on the last TabView.Item when another item is conditionally removed, they would be stuck past the last navigable TabView.Item. This was fixed by adding a useEffect function with a dependency on length that verifies whether the user's currentIndex is greater than the length - 1, and fixes it if not.
  3. Changes in the number of TabView.Items at indices less than the user's currentIndex will cause their TabView to change since all views will shift downward. This cannot be done from within the TabView component since there's no real way to know how the user's index should change from there. The users index will need to be managed outside the TabView component alongside the index state. This may require an update to the documentation, which I will be happy to add - just let me know.

@codecov
Copy link

codecov bot commented Mar 17, 2022

Codecov Report

Merging #3397 (a866cc1) into next (fc3bb93) will increase coverage by 0.12%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             next    #3397      +/-   ##
==========================================
+ Coverage   78.28%   78.41%   +0.12%     
==========================================
  Files          87       87              
  Lines        1750     1751       +1     
  Branches      772      772              
==========================================
+ Hits         1370     1373       +3     
+ Misses        375      373       -2     
  Partials        5        5              
Impacted Files Coverage Δ
packages/base/src/Tab/Tab.tsx 57.14% <100.00%> (+0.77%) ⬆️
packages/base/src/TabView/TabView.tsx 54.54% <100.00%> (+3.63%) ⬆️

📣 Codecov can now indicate which changes are the most critical in Pull Requests. Learn more

}
const position = dx / -window.width;
const next = position > value ? Math.ceil(position) : Math.floor(position);
onChange?.(currentIndex.current + next);
Copy link
Contributor Author

@WVAviator WVAviator Mar 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason this logic (75-77) broke with conditional children. I fixed by simply adding or subtracting 1 from the index based on the sign of dx

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a second look, the original code was setting "value" only once upon initial render when panResponder was initialized with useRef. Since value was, by default, set to 0 - this code was effectively doing the same thing as my fix. This code could have caused bugs in its current state - for example, if the developer mounted the component with a starting value for "value" of a number greater than 0, it could have prevented the index from being incremented since position would never be greater than value. The reason it broke with dynamic children was because "value" was permitted to update whenever the dependency on length was added.

Would it be worthwhile to consider added a minimum dx threshold to change TabViews? Right now, as long as Abs(dx) is greater than Abs(dy), even if it's a very small number, a pan will always initiate the view change. Is that desirable? Or should we be checking if the pan is more than, say, 25% of the screen width?

Copy link
Member

@arpitBhalla arpitBhalla Mar 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am making changes here #3380. Feel free to review it.

Copy link
Contributor Author

@WVAviator WVAviator Mar 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great - exactly what I was thinking of. It looks like to implement my fix in your new code, React.Children.count(children) on line 76 will need changed to React.Children.toArray(children).length, and that same value will need to be included in the dependency array on 101 as well as a dependency for panResponder which means converting it from useRef to useMemo.

Nice work.

return;
}

onChange?.(currentIndex.current + (dx > 0 ? -1 : 1));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works just as well as the previous logic (as tested in the example app) and is able to support conditional rendering.

return;
useEffect(() => {
if (currentIndex.current > length - 1) {
onChange(length - 1);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This keeps the user from getting stuck beyond the last view if length changes

@arpitBhalla arpitBhalla changed the title Conditional tabs fix(Tab): conditional rendering Mar 17, 2022
@arpitBhalla arpitBhalla modified the milestone: v4.0.0 Mar 17, 2022
@arpitBhalla arpitBhalla changed the title fix(Tab): conditional rendering fix(Tab, TabView): conditional rendering Mar 17, 2022
@arpitBhalla
Copy link
Member

@WVAviator Can you merge latest next

@WVAviator
Copy link
Contributor Author

@WVAviator Can you merge latest next

All set - tested again as well. Decided to omit the useEffect statement I had created before that kept the user from getting stuck past the last TabView - the index really needs to be managed in the parent component if/when the number of Views changes so I decided it wouldn't be a good idea to hardcode that behavior.

@arpitBhalla arpitBhalla merged commit 781e47a into react-native-elements:next Mar 19, 2022
@arpitBhalla
Copy link
Member

Thank you for your contribution 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Conditional rendering Tab.Item / TabView.Item

2 participants