Reputation: 2104
I knew this.refs are deprecated and can change it with React.createRef but in my case is different
This is our code
export default class ScrollableTabString extends Component {
static ITEM_PADDING = 15;
static defaultProps = {
tabs: [],
themeColor: '#ff8800',
};
static propTypes = {
tabs: PropTypes.any,
themeColor: PropTypes.string,
};
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.tabs !== this.props.tabs || this.state !== nextState) {
return true;
}
return false;
}
constructor(props) {
super(props);
this.views = [];
this.state = {};
this.scrollableTabRef = React.createRef();
}
componentDidMount() {
setTimeout(() => {
this.select(this.props.defaultSelectTab ? this.props.defaultSelectTab : 0);
}, 300);
}
select(i, code) {
let key = 'tab ' + i;
for (let view of this.views) {
if (view) {
let isSelected = false;
if (view.key === key) {
isSelected = true;
}
// This refs is Object with many ref views
if (this.refs[view.key]) {
this.refs[view.key].select(isSelected);
}
this.scrollableTabRef.current.goToIndex(i);
}
}
if (this.props.onSelected) {
this.props.onSelected(code);
}
}
render() {
this.views = [];
if (this.props.tabs) {
let tabs = this.props.tabs;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i]) {
let key = 'tab ' + i;
let view = (
<TabItem
column={tabs.length}
themeColor={this.props.themeColor}
useTabVersion2={this.props.useTabVersion2}
isFirstItem={i === 0}
isLastItem={i === tabs.length - 1}
ref={key}
key={key}
tabName={tabs[i].name}
onPress={() => {
this.select(i, tabs[i].code);
}}
/>
);
this.views.push(view);
}
}
}
let stypeTab = this.props.useTabVersion2
? null
: { borderBottomWidth: 0.5, borderColor: '#8F8E94' };
return (
<ScrollableTab
ref={this.scrollableTabRef}
style={Platform.OS === 'ios' ? stypeTab : null}
contentContainerStyle={Platform.OS === 'android' ? stypeTab : null}
>
{this.views}
</ScrollableTab>
);
}
}
I want to fix the warning from eslint but in our case, I can't use React.createRef
Upvotes: 2
Views: 1392
Reputation: 996
If your code is working fine and just want to suppress eslint warning,
please put this line on first line of your file: (I believe your eslint warning should be react/no-string-refs)
/* eslint react/no-string-refs: 0 */
If your case is you cannot use createRef()
, then try to achieve like this:
<ScrollableTab ref={(tab) => this.scrollableTab = tab} ...
And then called like this:
this.scrollableTab?.goToIndex(index);
After reading your sample codes, I suggest you to use state instead of string refs.
Also as there are a lot of redundancy code in your sample, I have tried to simplify it.
import React, { Component } from 'react';
import { Platform } from 'react-native';
import PropTypes from 'prop-types';
class ScrollableTabString extends Component {
static ITEM_PADDING = 15;
state = { activeTab: null }; scrollableTab;
stypeTab = (!this.props.useTabVersion2 ? { borderBottomWidth: 0.5, borderColor: '#8F8E94' } : null);
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.tabs !== this.props.tabs || this.state !== nextState);
}
componentDidMount() {
this.setState({ activeTab: this.props.defaultSelectTab || 0 });
}
/* If your second param - code is optional, add default value */
select = (index, code = null) => {
let key = `tab ${index}`;
/* Set activeTab state instead of calling function in every views */
this.setState({ activeTab: key });
this.scrollableTab?.goToIndex(index);
/* Not sure what are you archiving, but why don't you use component state and pass the isSelected into item */
/*for (let view of this.views) {
if (view) {}
}*/
this.props.onSelected && this.props.onSelected(code);
}
renderTabs = () => {
const { tabs, themeColor, useTabVersion2 } = this.props;
return tabs?.map((tab, index) => {
if (!tab) { return null; }
return (
<TabItem
key={`tab ${index}`}
column={tabs.length}
themeColor={themeColor}
useTabVersion2={useTabVersion2}
isFirstItem={index === 0}
isLastItem={index === (tabs.length - 1)}
tabName={tab.name}
onPress={() => this.select(index, tab.code)}
isSelected={this.state.activeTab === `tab ${index}`}
/>
);
});
};
render() {
return (
<ScrollableTab
ref={(tab) => this.scrollableTab = tab}
style={Platform.OS === 'ios' ? stypeTab : null}
contentContainerStyle={Platform.OS === 'android' ? stypeTab : null}
>
{this.renderTabs()}
</ScrollableTab>
);
}
}
ScrollableTabString.defaultProps = {
onSelected: null, /* Miss this props */
tabs: [],
themeColor: '#ff8800',
useTabVersion2: false /* Miss this props */
};
ScrollableTabString.propTypes = {
onSelected: PropTypes.func, /* Miss this props */
tabs: PropTypes.any,
themeColor: PropTypes.string,
useTabVersion2: PropTypes.bool /* Miss this props */
};
export default ScrollableTabString;
Using select()
with TabItem may leads to several issues:
select()
will be called as many times as number of TabItems, to change every state inside)I strongly suggest to set state inside parent component and pass it as props to child. To explain clearly how it works, here is an simplest sample for you:
ScrollableTabString.js
class ScrollableTabString extends Component {
state = { activeTab: null };
selectTab = (tabName) => { this.setState({ activeTab: tabName }); };
renderTabs = () => this.props.tabs?.map((tab, index) => (
<TabItem key={`tab ${index`} tabName={tab.name} onPress={this.selectTab} activeTab={this.state.activeTab} />
));
}
TabItem.js
class TabItem extends Component {
/* Use this function on Touchable component for user to press */
onTabPress = () => this.props.onPress(this.props.tabName);
render() {
const { activeTab, tabName } = this.props;
const isThisTabActive = (activeTab === tabName);
/* Use any props and functions you have to achieve UI, the UI will change according to parent state */
return (
<TouchableOpacity onPress={this.onTabPress} style={isThisTabActive ? styles.activeTab : styles.normalTab}>
<Text>{tabName}</Text>
</TouchableOpacity>
);
}
}
If you do really have some state needed to be change inside TabItem
, try using componentDidUpdate()
in there, but it should be also a redundancy codes:
componentDidUpdate(prevProps) {
if (this.props.activeTab !== prevProps.activeTab) {
// change your state here
}
}
Upvotes: 2