Consider renaming `initialPage`
grabbou opened this issue ยท 22 comments
It's actually currentPage
since it reacts to changes https://github.com/brentvatne/react-native-scrollable-tab-view/blob/master/index.js#L55 (or maybe at least clarify that in docs?)
absolutely agree
It depends on expected by user behaviour, we can either:
- revert old
initialPage
behaviour & implementpage
prop - clarify that in docs, make
page
prop and log thatinitialPage
is deprecated and will be removed with next version
First option is better for me.
So what was the conclusion here - how can this problem of popping back to the initial set page be solved?
@robtg4 i think it's not the component job to do so it's the user implementation to decide the initial state using redux or what ever he wants
UPDATE 2 - solution
I returned to this problem again and found simple solution.
For control with Redux we don`t need use page
- we need goToPage
(how could i miss it before?)
UPDATE
All of i wrote next will work only for controlled tabs with Redux, but i catch scroll back if just swipe to other tab (if change with Redux or tap on TabBar all work fine).
But for control with Redux next solution work well.
TL;DR
Add identical initialPage
and on start, don`t change page
initialPage
, change only ;page
Update don`t use page
- only initialPage
on start and goToPage
for change page.
I use redux for save my current tab (I change it in other place).
I want change active tab in drawer if that route active or initial specific tab if go from other route.
constructor() {
super();
this.pageProps = {} // can declare just in class
this.pageIndex = null // ** Update **
}
Ok we have object for save component`s page data;
!important; keep it in mind! For initial Tabs we need set identical initialPage
and (use page
goToPage
, if use page
u can catch slide back). But for change tab we don`t need change initialPage
. If we change initialPage
without unmount component or set different values - tabs jump out of range and tab will not be loaded -_-
handleChangeScreen = ({ i }) => {
this.pageIndex = i; // **Update ** save current page index
this.props.dispatch(setMainScreen(i));
}
just handle onChangeTab and set in Redux (mainScreen
- name of var for current tab in Redux)
Next step:
if (!this.pageProps.initialPage) this.pageProps.initialPage = mainScreen;
mainScreen
- index current tab. Don`t change initialPage
if tabs already init.
if (!this.pageProps.page || !this.pageProps.page !== mainScreen) this.pageProps.page = mainScreen;
Set Don`t use this codepage
for init or change if page changed in Redux.
Update
Now we need catch receive new mainScreen if it change outside
componentWillReceiveProps(nextProps) {
const { YouStore: { mainScreen } } = nextProps;
if ( this.props.YouStore.mainScreen !== mainScreen && mainScreen !== this.pageIndex) {
if (this.tabView) this.tabView.goToPage(mainScreen)
}
}
<ScrollableTabView
{...this.pageProps}
onChangeTab={this.handleChangeScreen}
ref={(tabView) => { this.tabView = tabView; }} // set tabView
...
/>
Add props to ScrollableTabView
. Have fun.
I try explain in detail, perhaps i can`t did it =).
i think many components has an issue with interacting with redux the main reason is sometime you need redux to control the component and sometime you need the component to control redux maybe all components should consider doing the following scenario:
- from user interaction (e.g click, slide)
==run==> internal event handler (this.onClick
) - from internal handler
==if external handler exits ==> run (this.props.onClick
) - after external handler call
== if return true ==> change internal state (this.setState({page})
)
== if return false ==> do nothing - listen for props (e.g
componentWillReceiveProps
)
== if prop not equal state ==> change internal state (this.setState({page:this.props.page})
)
in this way you can listen to the event cancel it and change your redux state in the other hand redux will change your component prop again :)
@digital-flowers yeah, i agree, that will be awesome. If we don`t control component - just do u job, but if we do - doesn`t disturb =)
exactly !
UPDATE
I returned to this problem again and found simple solution.
For control with Redux we don`t need use page - we need goToPage (how could i miss it before?)
I updated my past solution.
Sorry for long read
@dictory Could you elaborate how you would use goToPage
in a redux setup? I can't really figure out what you mean is your solution in the previous comment with all those updates :)
@Froelund, my post with update actual now(doesn't content old code).
That work for me on iOS. Have no idea about android - test it )
Save actual page index (or name if you want) to Redux store. Two point:
- In component with tabs:
handleChangeScreen = () => {
this.pageIndex = i; // save local too
this.props.dispatch(saveCurrentTabToRedux(i));
}
...
<ScrollableTabView
...
onChangeTab={this.handleChangeScreen}
>
- Change page outside (for example):
this.props.dispatch(saveCurrentTabToRedux(2));
Next. Add ref
to ScrollableTabView
<ScrollableTabView
...
ref={(tabView) => { this.tabView = tabView; }}
>
Detect page change outside:
componentWillReceiveProps(nextProps) {
const { YouStore: { pageIndex } } = nextProps;
if ( this.props.YouStore.mainScreen !== pageIndex && pageIndex !== this.pageIndex) {
if (this.tabView) this.tabView.goToPage(pageIndex)
}
}
You are awesome. Don`t forget set initialPage.
@dictory :
Hi, I did as you say.
But I realized that by using, onChangeTab, I undergo some slowdowns, then the parameter that returns the function is an Obj not an int.
@grabbou , @digital-flowers : So I ask is there a way to know index of the tab that is selected in every moment, how can I do, I do not know using its reference by chance?
@dictory
It worked like a charm
Just one thing, in this section:
handleChangeScreen = () => { this.pageIndex = i; // save local too this.props.dispatch(saveCurrentTabToRedux(i)); }
the variable "i" is not mentioned where it comes from, should be
handleChangeScreen = ({i}) => { this.pageIndex = i; // save local too this.props.dispatch(saveCurrentTabToRedux(i)); }
in @dictory example instead of using the local variable this.pageIndex
you can just use this.tabView.state.currentPage
@dictory I am finding it hard to understand your instructions. Can you please provide an Expo snack for this?
@newCaoTao, @khat33b last year I work on non react-native
project.
But I find this:
class MainScreen extends React.Component {
tabView = null;
loadCount = 0;
screenKey = null;
initialPage = null;
screens = [
{
key : 'tags',
component: () => <TagsListAll key="tags" tabLabel="#" />,
},
...
]
componentWillReceiveProps(nextProps) {
// mainScreen from store
const { mainScreen } = nextProps;
if (
this.props.mainScreen !== mainScreen && mainScreen !== this.screenKey &&
this.loadCount > 2 && this.screenKey !== null && !!this.tabView
) this.tabView.goToPage(this.getScreenIndex(nextProps));
}
getScreenIndex = (props = this.props) => findIndex(this.screens, ['key', props.mainScreen]);
renderTabBar = () => (
<ScrollableTabBar
style={styles.tabBar}
tabsContainerStyle={styles.tabsContainer}
tabStyle={styles.tab}
/>
)
handleChangeScreen = ({ i }) => {
this.screenKey = (this.screens[i] && this.screens[i].key) || (this.otherPosts[i] && this.otherPosts[i].key);
this.props.dispatch(setMainScreen(this.screenKey));
}
getList = list => map(list, screen => screen.component({ settings: this.props.Settings }))
render() {
const { mainScreen } = this.props;
const screens = this.getList(this.screens.concat(this.otherPosts));
this.loadCount += 1; // hook strange bug
if (this.initialPage === null) {
const screenIndex = this.getScreenIndex();
this.screenKey = mainScreen;
this.initialPage = screenIndex;
}
return (
<ScrollableTabView
initialPage={this.initialPage}
onChangeTab={this.handleChangeScreen}
renderTabBar={this.renderTabBar}
ref={(tabView) => { this.tabView = tabView; }}
...
>
{screens}
</ScrollableTabView>
);
}
}
export default MainScreen;
I recommend use functional component and useEffect
instead componentWillReceiveProps
.
componentWillReceiveProps
having trouble with the StateLess component.
when the onChangeTab trigger. it re-redner the component twice .
not sure why