How to convert this React hook component into a functional component - javascript

Below is the code I want converted to hooks -
const App = () => {
const [visible, setVisible] = React.useState(false);
const openMenu = () => setVisible(true);
const closeMenu = () => setVisible(false);
const [barClicked, setbarClicked] = React.useState(false);
const [lineClicked, setlineClicked] = React.useState(false);
const [pieClicked, setpieClicked] = React.useState(false);
const BarCharts = () => {
const fill = 'rgb(134, 65, 244)'
const data = [50, 10, 40, 95, -4, -24, null, 85, undefined, 0, 35, 53, -53, 24, 50, -20, -80]
return (
<View style={styles.sectionContainer}>
<BarChart style={{ height: 200 }} data={data} svg={{ fill }} contentInset={{ top: 30, bottom: 30 }}>
<Grid />
</BarChart>
</View>
);
};
const LineCharts: () => React$Node = () => {
const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]
return (
<View style={styles.sectionContainer}>
<LineChart
style={{ height: 200 }}
data={data}
svg={{ stroke: 'rgb(134, 65, 244)' }}
contentInset={{ top: 20, bottom: 20 }}
>
<Grid />
</LineChart>
</View>
);
};
const PieCharts: () => React$Node = () => {
const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]
const randomColor = () => ('#' + ((Math.random() * 0xffffff) << 0).toString(16) + '000000').slice(0, 7)
const pieData = data
.filter((value) => value > 0)
.map((value, index) => ({
value,
svg: {
fill: randomColor(),
onPress: () => console.log('press', index),
},
key: `pie-${index}`,
}))
return (
<PieChart style={{ height: 200 }} data={pieData} />
);
};
return (
<Provider>
<View
style={{
paddingTop: 50,
flexDirection: 'row',
justifyContent: 'center',
}}>
<Menu
visible={visible}
onDismiss={closeMenu}
anchor={<Button onPress={openMenu}>Show menu</Button>}>
<Menu.Item onPress={() => setbarClicked(!barClicked)} title="Item 1" />
<Menu.Item onPress={() => setlineClicked(!lineClicked)} title="Item 2" />
<Menu.Item onPress={() => setpieClicked(!pieClicked)} title="Item 3" />
</Menu>
</View>
<View>
<Button onPress={() => setbarClicked(!barClicked)}></Button>
{barClicked && <BarCharts />}
<Button onPress={() => setlineClicked(!lineClicked)}></Button>
{lineClicked && <LineCharts />}
<Button onPress={() => setpieClicked(!pieClicked)}></Button>
{pieClicked && <PieCharts />}
</View>
<View style={styles.container}>
</View>
</Provider>
);
};
export default App;
Here's what it's suppose to do upong clicking SHOW MENU > Item 1. I tried doing it myself, but it's returning errors. - 'Can't find variable barClicked'. Below is what I came up with
export default class MyComponent extends React.Component {
state = {
visible: false,
barClicked: false,
lineClicked: false,
pieClicked: false
};
_openMenu = () => this.setState({ visible: true });
_closeMenu = () => this.setState({ visible: false });
_setbarClicked = () => this.setState({ barClicked: true });
_setlineClicked = () => this.setState({ barClicked: true });
_setpieClicked = () => this.setState({ barClicked: true });
render() {
return (
<Provider>
<View
style={{
paddingTop: 50,
flexDirection: 'row',
justifyContent: 'center'
}}>
<Menu
visible={this.state.visible}
onDismiss={this._closeMenu}
anchor={
<Button onPress={this._openMenu}>Show menu</Button>}>
<Menu.Item onPress={() => this._setbarClicked(!barClicked)} title="Item 1" />
<Menu.Item onPress={() => this._setlineClicked(!lineClicked)} title="Item 2" />
<Menu.Item onPress={() => this._setpieClicked(!pieClicked)} title="Item 3" />
</Menu>
</View>
<View>
<Button onPress={() => this._setbarClicked(!barClicked)}></Button>
{this.barClicked && <BarCharts />}
<Button onPress={() => this._setlineClicked(!lineClicked)}></Button>
{this.lineClicked && <LineCharts />}
<Button onPress={() => this._setpieClicked(!pieClicked)}></Button>
{this.pieClicked && <PieCharts />}
</View>
</Provider>
);
}
}
I have used BarChart, LineChart and PieChart from react-native-svg-chart library.

Issues
You don't reference any of your state variables correctly and your handlers don't consume any arguments. All your handlers also incorrectly reference your barClicked state.
_setbarClicked = () => this.setState({ barClicked: true });
_setlineClicked = () => this.setState({ barClicked: true });
_setpieClicked = () => this.setState({ barClicked: true });
Solution
If you want your handlers to take a parameter then adjust the implementation as follows:
_setbarClicked = (barClicked) => this.setState({ barClicked });
_setlineClicked = (lineClicked) => this.setState({ lineClicked });
_setpieClicked = (pieClicked) => this.setState({ pieClicked });
And adjust your callbacks to reference state correctly, i.e. this.state. barClicked.
render() {
const { barClicked, lineClicked, pieClicked } = this.state;
return (
...
<Menu.Item onPress={() => this._setbarClicked(!barClicked)} title="Item 1" />
<Menu.Item onPress={() => this._setlineClicked(!lineClicked)} title="Item 2" />
<Menu.Item onPress={() => this._setpieClicked(!pieClicked)} title="Item 3" />
...
<Button onPress={() => this._setbarClicked(!barClicked)}></Button>
{barClicked && <BarCharts />}
<Button onPress={() => this._setlineClicked(!lineClicked)}></Button>
{lineClicked && <LineCharts />}
<Button onPress={() => this._setpieClicked(!pieClicked)}></Button>
{pieClicked && <PieCharts />}
...
);
}
Suggestion
Since you are really just toggling these state values just do that in the handlers.
_setbarClicked = () => this.setState(prevState => ({ !prevState.barClicked }));
_setlineClicked = () => this.setState(prevState => ({ !prevState.lineClicked }));
_setpieClicked = () => this.setState(prevState => ({ !prevState.pieClicked }));
Then just attach as non-anonymous callback.
render() {
const { barClicked, lineClicked, pieClicked } = this.state;
return (
...
<Menu.Item onPress={this._setbarClicked} title="Item 1" />
<Menu.Item onPress={this._setlineClicked} title="Item 2" />
<Menu.Item onPress={this._setpieClicked} title="Item 3" />
...
<Button onPress={this._setbarClicked}></Button>
{barClicked && <BarCharts />}
<Button onPress={this._setlineClicked}></Button>
{lineClicked && <LineCharts />}
<Button onPress={this._setpieClicked}></Button>
{pieClicked && <PieCharts />}
...
);
}

You need to place the state object in constructor (preferably) in class components.
export default class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
visible: false,
barClicked: false,
lineClicked: false,
pieClicked: false
};
}
...
and change your calls from this._setbarClicked(!barClicked) to this._setbarClicked(!this.state.barClicked)

Related

VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders

Im working on a react-native project and what I'm trying to do is for the user to have the possibility to select phone numbers in his contact list.
When the user selects one or more contacts, the app won't work, and it shows this error on the console: VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc.
ContactList.js
unction ContactList() {
const [refreshing, setRefreshing] = React.useState(false);
const [itemChecked, setItemChecked] = useState([]);
const [checked, setChecked] = useState(new Map());
const [contacts, setContacts] = useState([]);
const [filter, setFilter] = useState([]);
const [search, setSearch] = useState('');
const [data, setData] = useState(filter)
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
if (status === 'granted') {
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.PhoneNumbers],
// fields: [Contacts.Fields.Name],
});
if (data.length > 0) {
setContacts(data);
setFilter(data);
// console.log('contact', contacts[1]);
// console.log('filter', filter);
}
}
})();
}, []);
const searchFilter = (text) => {
if (text) {
const newData = contacts.filter((item) => {
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilter(newData);
setSearch(text);
} else {
setFilter(contacts);
setSearch(text);
}
};
const onChangeValue = (item) => {
checked.set(item, true);
};
useEffect(() => {
checked &&
setData((previous) => [...previous, {phone: contacts} ])
}, [checked],
)
const renderItem = ({ item, index }) => {
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{ flexDirection: 'row', flex: 1 }}>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey', marginBottom: 15 }}>
<Text onPress={() => setChecked(true)} style={{ fontSize: 20, marginHorizontal: 10 }}>
{item.name + ' '}
</Text>
<Text style={{ fontSize: 17, marginHorizontal: 10, marginTop: 5, color: 'grey' }}>
{item.phoneNumbers && item.phoneNumbers[0] && item.phoneNumbers[0].number}
</Text>
</View>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey' }}>
<CheckBox
style={{ width: 15, height: 15 }}
right={true}
checked={checked.get(index)}
onPress={()=> onChangeValue(index)}
/>
</View>
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<View
style={{
height: 40,
justifyContent: 'center',
backgroundColor: '#EEEEEE',
width: '90%',
marginHorizontal: 20,
marginTop: 15,
borderRadius: 10,
}}
>
<Feather name="search" size={20} color="grey" style={{ position: 'absolute', left: 32 }} />
<TextInput
placeholder="Search"
placeholderTextColor="#949494"
style={{
left: 20,
paddingHorizontal: 35,
fontSize: 20,
}}
value={search}
onChangeText={(text) => {
searchFilter(text);
setSearch(text);
}}
/>
</View>
<FlatList
style={{ marginTop: 15 }}
data={contacts && filter}
keyExtractor={(item) => `key-${item.id.toString()}`}
renderItem={renderItem}
ListEmptyComponent={<Text message="No contacts found." />}
/>
</View>
</SafeAreaView>
);
}
export default ContactList;
How can I solve this bug?

How to make reusable component in react-native and call them inside other component?

I have a modal component that I want to reuse in future component.
For this reason I want to have it in its own file.
I don't manage to call the component inside the other properly though.
Here is my last attempt of it.
The Modal component code:
export const FlyingDescription = (cityDescription) => {
const [modalVisible, setModalVisible] = useState(false);
return (
<View>
<Modal
animation="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => setModalVisible(!modalVisible)}
>
<View style={styleBoxParent}>
<View style={styleBoxChildren}>
<LinearGradient
style={styleLinearGradient}
colors={['#136a8a', '#267871']}
start={[0, 0.65]}
>
<Text style={styleText}>{cityDescription}</Text>
<Pressable
onPress={() => setModalVisible(!modalVisible)}
>
<Text style={styleText}>Close</Text>
</Pressable>
</LinearGradient>
</View>
</View>
</Modal>
</View>
);
};
The component where I want to call the Modal component:
import FlyingDescription from './FlyingDescription.js';
var utils = require('./utils');
export default class SearchScreen extends React.Component {
constructor(props) {
super(props);
this.navigation = props.navigation;
this.state = {
searchInput: '',
item : {},
renderSearch: false,
renderFlyingDescription: false,
};
this.errorMessage = 'Search for cities...';
}
resetState = () => {
this.setState({
item: {},
renderable: false,
}); }
setSearchInput = (value) => {
this.setState({
searchInput: value
});
}
searchCity = () => {
this.resetState();
utils.fetchWeather(this.state.searchInput).then(response => {
if (response.cod == 200) {
console.log(response);
this.setItemState(
{
name: response.name,
temp: Math.ceil(response.main.temp),
type: response.weather[0].main,
desc: 'Humidity: ' + response.main.humidity + '% - ' + response.weather[0].main
}
);
this.setRenderSearch();
}
});
}
setItemState = (newItem) => {
this.setState(
{
item: newItem,
}
);
}
setRenderSearch = () => {
this.setState(
{
renderSearch: true,
}
);
}
render = () => {
return(
<View style={utils.style.container}>
<StatusBar barStyle="light-content" />
<Text style={utils.style.titleContainer}>☀️ CityWeather</Text>
<View style={{alignItems: 'center', width:'90%'}}>
<Text>Search for a city</Text>
<TextInput
onChangeText={(value) => this.setSearchInput(value)}
value={this.searchInput}
style={{ width: '80%', padding: 15, margin: 5, backgroundColor: 'black', color: 'white' }}
/>
<TouchableHighlight
style={{backgroundColor: 'grey', padding: 20, borderRadius: 8}}
onPress={this.searchCity}
>
<Text style={{fontSize: 14, color:'white'}}>Search</Text>
</TouchableHighlight>
</View>
{ this.state.renderSearch ? (
<TouchableHighlight
underlayColor="white"
onPress={ () => this.setState({renderFlyingDescription: true})}
>
<LinearGradient
colors={['rgba(0,0,0,0.05)', 'rgba(0,0,0,0)']}
start={[0, 0.5]}
>
<View style={utils.style.row}>
<Text style={[utils.getTempRange(this.state.item.temp), utils.style.temp]}>
{utils.getEmoji(this.state.item.type)} {this.state.item.temp} °C
</Text>
<Text style={utils.style.cityName}>{this.state.item.name}</Text>
</View>
</LinearGradient>
</TouchableHighlight>
) : (
// WHERE I WANT TO RENDER MY MODAL COMPONENT
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center', fontSize: 32}}>
<Text>{this.errorMessage}</Text>
</View>
)
}
{
renderFlyingDescription(this.state.renderFlyingDescription, this.state.item.desc)
}
</View>
);
}
}
export function renderFlyingDescription(isInterpretable, cityDescription) {
if(isInterpretable) {
return <FlyingDescription cityDescription={cityDescription} />
}
}

Converting class components to hooks form in react native

Not sure how I would convert this class component to hooks form. I tried to, but the app doesn't run the same.
Here's the original code written as class components-
class Area extends React.PureComponent {
state = {
data: [],
tooltipX: null,
tooltipY: null,
tooltipIndex: null,
};
componentDidMount() {
this.reorderData();
}
reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
this.setState({
data: reorderedData,
});
};
render() {
const { data, tooltipX, tooltipY, tooltipIndex } = this.state;
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
}
This code is being used to integrate tooltip in react native charts. I want to include this code with rest of the project code written in hooks form.
export default class myPureComponent extends React.PureComponent
is equal to:
export default React.memo(()=>{
return <View>//...</view>
},(prevProps, nextProps) => {
return prevProps.x === nextProps.x;
// the component will be updated only on `x` prop changes.
})
componentDidMount is equal to:
React.useEffect(()=>reorderData(),[]);
Try to format your codes properly.
Add a [] in your use effects to it only render once.
useEffect(() => {
reorderData();
},[]); //add [] as a dependency, this renders useEffect only on mount.
Full codes
const Area = () => {
const [ data, setData ] = useState([]);
const [ tooltipX, setTooltipX ] = useState(null);
const [ tooltipY, setTooltipY ] = useState(null);
const [ tooltipIndex, setTooltipIndex ] = useState(null);
useEffect(() => reorderData(),[]);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}

Convert class component to hooks

I am attempting to convert the following code snippet to hooks. What I have converted so far, doesn't render output as the original. Below is the default code written as class components-
class Area extends React.PureComponent {
state = {
data: [],
tooltipX: null,
tooltipY: null,
tooltipIndex: null,
};
componentDidMount() {
this.reorderData();
}
reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
this.setState({
data: reorderedData,
});
};
render() {
const { data, tooltipX, tooltipY, tooltipIndex } = this.state;
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
}
Below's the code I converted. I feel the below code is not rendering the tooltip because of how the state is written out in hooks. The tooltip isn't showing up upon click.
const Area = () => {
const [ data, setData ] = useState([]);
const [ tooltipX, setTooltipX ] = useState(null);
const [ tooltipY, setTooltipY ] = useState(null);
const [ tooltipIndex, setTooltipIndex ] = useState(null);
useEffect(() => reorderData(),[]);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
export default Area = () => {
const [data, setData] = useState([]);
const [tooltipX, setTooltipX] = useState(null);
const [tooltipY, setTooltipY] = useState(null);
const [tooltipIndex, setTooltipIndex] = useState(null);
useEffect(() => reorderData(), []);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() => {
// try this 👇
setTooltipX(moment(item.date));
setTooltipY(item.score);
setTooltipIndex(index);
// try this ☝
}}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: "70%" }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: "#003F5A" }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid
svg={{ stroke: "rgba(151, 151, 151, 0.09)" }}
belowChart={false}
/>
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: "50%",
justifyContent: "center",
alignItems: "center",
}}
>
<Text
style={{
fontSize: 18,
color: "#ccc",
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
};

How to pass params from one screen to another via reactnavigation v5

I have an issue: thre is a list of users, and I want to add appointment to any user, so on user screen a PlusButton sending user on the add appointment screen (the PlusButton rendered from headerRight of the user screen). BUT I dont know how to pass to AddAppointmentScreen users id, because link to screen not via button, its via react-navigation v5.
here is a link to my project on github
App.js
const HeaderRight = ({ navigation, target }) => {
return (
<Button transparent onPress={() => navigation.navigate(target)}>
<Icon name='plus' type='Entypo' style={{ fontSize: 26 }} />
</Button>
)
}
<Stack.Screen
name='Patient'
component={PatientScreen}
options={{
title: 'Карта пациента',
headerTintColor: '#2A86FF',
headerTitleAlign: 'center',
headerTitleStyle: {
fontWeight: 'bold',
fontSize: 20,
},
headerBackTitleVisible: false,
headerRight: () => <HeaderRight navigation={navigation} target={'AddAppointment'} />,
}}
/>
AddAppointmentScreen.js
import React, { useState } from 'react'
import { Keyboard } from 'react-native'
import { Container, Content, Form, Item, Input, Label, Icon, Text, Button } from 'native-base'
import DateTimePicker from '#react-native-community/datetimepicker'
import styled from 'styled-components/native'
import { appointmentsApi } from '../utils/api'
const AddAppointmentScreen = ({ route, navigation }) => {
const [values, setValues] = useState({ patientId: route.params?.patientId._id ?? 'defaultValue' })
const [date, setDate] = useState(new Date())
const [mode, setMode] = useState('date')
const [show, setShow] = useState(false)
console.log(values.patientId)
//I need to get patientId in this screen to create appointment to THIS user with its ID
const setFieldValue = (name, value) => {
setValues({
...values,
[name]: value,
})
}
const handleChange = (name, e) => {
const text = e.nativeEvent.text
setFieldValue(name, text)
}
const submitHandler = () => {
appointmentsApi
.add(values)
.then(() => {
navigation.navigate('Patient')
})
.catch((e) => {
alert(e)
})
/* alert(JSON.stringify(values)) */
}
const formatDate = (date) => {
var d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear()
if (month.length < 2) month = '0' + month
if (day.length < 2) day = '0' + day
return [year, month, day].join('-')
}
const onChange = (event, selectedDate) => {
Keyboard.dismiss()
const currentDate = selectedDate || date
const date = formatDate(currentDate)
const time = currentDate.toTimeString().split(' ')[0].slice(0, 5)
setShow(Platform.OS === 'ios')
setDate(currentDate)
setValues({
...values,
['date']: date,
['time']: time,
})
}
const showMode = (currentMode) => {
show ? setShow(false) : setShow(true)
setMode(currentMode)
}
const showDatepicker = () => {
Keyboard.dismiss()
showMode('datetime')
}
return (
<Container>
<Content style={{ paddingLeft: 20, paddingRight: 20 }}>
<Form>
<Item picker style={{ borderWidth: 0 }} /* floatingLabel */>
<Input
onChange={handleChange.bind(this, 'dentNumber')}
value={values.dentNumber}
keyboardType='number-pad'
clearButtonMode='while-editing'
placeholder='* Номер зуба'
/>
</Item>
<Item picker>
<Input
onChange={handleChange.bind(this, 'diagnosis')}
value={values.diagnosis}
clearButtonMode='while-editing'
placeholder='* Диагноз'
/>
</Item>
<Item picker>
<Input
onChange={handleChange.bind(this, 'description')}
value={values.description}
multiline
clearButtonMode='while-editing'
placeholder='Подробное описание или заметка'
style={{ paddingTop: 15, paddingBottom: 15 }}
/>
</Item>
<Item picker>
<Input
onChange={handleChange.bind(this, 'price')}
value={values.price}
keyboardType='number-pad'
clearButtonMode='while-editing'
placeholder='* Цена'
/>
</Item>
<ButtonRN onPress={showDatepicker}>
<Text style={{ fontSize: 18, fontWeight: '400' }}>
Дата: {formatDate(date)}, время: {date.toTimeString().split(' ')[0].slice(0, 5)}
</Text>
</ButtonRN>
{show && (
<DateTimePicker
timeZoneOffsetInSeconds={21600}
minimumDate={new Date()}
value={date}
mode={mode}
is24Hour={true}
display='default'
locale='ru-RU'
minuteInterval={10}
onChange={onChange}
/>
)}
<ButtonView>
<Button
onPress={submitHandler}
rounded
block
iconLeft
style={{ backgroundColor: '#84D269' }}
>
<Icon type='Entypo' name='plus' style={{ color: '#fff' }} />
<Text style={{ color: '#fff' }}>Добавить прием</Text>
</Button>
</ButtonView>
<Label style={{ marginTop: 10, fontSize: 16 }}>
Поля помеченные звездочкой <TomatoText>*</TomatoText> обязательны для заполнения
</Label>
</Form>
</Content>
</Container>
)
}
const ButtonRN = styled.TouchableOpacity({
paddingTop: 15,
paddingLeft: 5,
})
const ButtonView = styled.View({
marginTop: 15,
})
const TomatoText = styled.Text({
color: 'tomato',
})
export default AddAppointmentScreen
I have not gone through your code but I can tell you how can you pass params to next screen as per your heading.
toNavigate = (user) => {
this.setState({ isLoading: false },
() => { this.props.navigation.push('Main', { userData: user }) })
//I am sending user into userData to my Main screen
}
And I am receive it like
render(){
user = this.props.route.params.userData;
}
If any issue let me know.
Hope it helps!!!
This can be done using the useNavigation hook as you are inside the Navigation Container.
import { NavigationContainer, useNavigation } from '#react-navigation/native';
const HeaderRight = ({ navigation, target }) => {
const navigation = useNavigation();
return (
<Button transparent onPress={() => navigation.navigate(target)}>
<Icon name='plus' type='Entypo' style={{ fontSize: 26 }} />
</Button>
)
}
And you have to pass the target like below, as navigation is not accessible from there there is no point on passing that anyway.
headerRight: () => <HeaderRight target={'Profile'} />,
Hope this helps.
I solved it like this:
getting params in StackScreen - you need to pass to options route and navigation, and then you can get the route params like below:
<Stack.Screen
name='Patient'
component={PatientScreen}
options={({ route, navigation }) => {
return {
title: 'Карта пациента',
headerTintColor: '#2A86FF',
headerTitleAlign: 'center',
headerTitleStyle: {
fontWeight: 'bold',
fontSize: 20,
},
headerBackTitleVisible: false,
headerRight: () => (
<HeaderRight target={'AddAppointment'} patientId={route.params.patientId} />
),
}
}}
/>
then I get patientId in HeaderRight component and pass it to new screen, with second parameter with navigation like this (of course there is option to not use this component and pass it from Stack.Screen, but I did it to my own purpose):
const HeaderRight = ({ patientId, target }) => {
const navigation = useNavigation()
return (
<Button transparent onPress={() => navigation.navigate(target, { patientId })}>
<Icon name='plus' type='Entypo' style={{ fontSize: 26 }} />
</Button>
)
}
I get patientId in this third screen like below:
patientId: route.params?.patientId ?? '',

Categories