I try to use Animated
API from react-native
. I want to make a component that looks like accordion (this is for sake of try Animated
API. I know there are other library for accordion that I could use). The animation works, but the toValue
not expanding as the height of the content inside. Here is what I've try so far:
Child.js
import React, { Component } from 'react';
import {
Animated,
View,
} from 'react-native';
class Child extends Component {
constructor(props) {
super(props);
this.state = {
expanded: false,
animation: null,
maxHeight: '',
minHeight: '',
};
}
toggle = () => {
const {
expanded, maxHeight, minHeight,
} = this.state;
const initialValue = expanded ? maxHeight + minHeight : minHeight;
const finalValue = expanded ? minHeight : maxHeight + minHeight;
this.setState(prevState => ({
expanded: !prevState.expanded,
}));
this.state.animation.setValue(initialValue);
Animated.spring(
this.state.animation,
{
toValue: finalValue,
bounciness: 0,
},
).start();
}
setMinHeight = (e) => {
this.setState({
minHeight: e.nativeEvent.layout.height,
animation: new Animated.Value(e.nativeEvent.layout.height),
});
}
setMaxHeight = (e) => {
this.setState({
maxHeight: e.nativeEvent.layout.height,
});
}
render() {
const { header, content } = this.props;
const { animation } = this.state;
return (
<Animated.View style={{
height: animation,
overflow: 'hidden',
width: '100%',
}}
>
<View onLayout={e => this.setMinHeight(e)}>
{header}
</View>
<View onLayout={e => this.setMaxHeight(e)}>
{content}
</View>
</Animated.View>
);
}
}
export default Child;
App.js
import React, { Component, Fragment } from 'react';
import {
ScrollView,
View,
Text,
StyleSheet,
Button,
} from 'react-native';
import { Row } from 'react-native-easy-grid';
// You can import from local files
import Child from './components/Child';
export default class App extends React.Component {
render() {
const header = (
<Button
title="press"
onPress={() => this.toggle.toggle()}
/>
);
const content = (
<Fragment>
<View
style={{
width: 300,
backgroundColor: 'green',
padding: 20,
}}
>
<View style={{ width: 'auto', padding: 10, backgroundColor: 'blue' }}>
<Text>Content1</Text>
<Text>Content2</Text>
<Text>Content3</Text>
// Please try to add more text here
</View>
</View>
</Fragment>
);
return (
<Fragment>
<ScrollView style={[styles.container]}>
<Row style={{ marginTop: 30 }}>
<View style={[styles.view]}>
<Child
content={content}
header={header}
ref={(ref) => { this.toggle = ref; }}
/>
</View>
</Row>
</ScrollView>
</Fragment>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 15,
backgroundColor: 'purple',
},
view: {
flex: 1,
backgroundColor: 'pink',
},
});
What I miss here? Any help would be so helpful and appreciated. Thank you!
Update
When I try to put console.log(e.nativeEvent.layout.height)
inside setMaxHeight
, it sometimes show different value. How this could happen?
as @hardworker say in the comment, so I try to put validation before setState of maxHeight and minHeight. To prevent the state of maxHeight and minHeight changing after initial render. Here is the working code:
setMinHeight = (e) => {
if(this.state.minHeight === '') {
this.setState({
minHeight: e.nativeEvent.layout.height,
animation: new Animated.Value(e.nativeEvent.layout.height),
});
}
}
setMaxHeight = (e) => {
if (this.state.maxHeight === '') {
this.setState({
maxHeight: e.nativeEvent.layout.height,
});
}
}