React Native animation value 'auto' - react-native

const heightAnim = useRef(new Animated.Value(0)).current
const heightInterpolate = heightAnim.interpolate({
inputRange: [0,1],
outputRange: ['auto',500],
})
The above code not working because error invalid pattern. 'auto' is
String and 500 is number.
I try to set
const value2 = 500,
outputRange: ['auto',value2],
but as I expected it didn't work
How can set Value of animated to 'auto'?

Here is an example using LayoutAnimation
if (Platform.OS === "android") {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
.....
const App = () => {
.....
<View style={styles.header}>
<TouchableOpacity
onPress={() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpanded(!expanded);
}}
>
<Text>Press me to {expanded ? "collapse" : "expand"}!</Text>
</TouchableOpacity>
<View
style={{ backgroundColor: "red", height: expanded ? "auto" : 500 }}
>
<Text>Test</Text>
<Text>Test</Text>
<Text>Test</Text>
<Text>Test</Text>
<Text>Test</Text>
</View>
</View>
....
We can call ConfigureNext with 3 presets
LayoutAnimation.Presets.easeInEaseOut
LayoutAnimation.Presets.linear
LayoutAnimation.Presets.spring
You can even create your custom confige
ConfigureNext({ duration: 700, update: { type: 'spring', springDamping: 0.4 },})
Check Documentation for more information

Related

How to use navigation.navigate() in map function

I'm trying to display a list of TouchableOpacity to navigate to an other page with navigation.navigate(...).
I'm using map to display the list but I can't navigate, the onPress don't work with this error message : "undefined is not an object (evaluating 'navigation.navigate')
What am I doing wrong ?
function PopupMenuAction ({navigation}) {
return (
<>
<TouchableOpacity style={style.shape1to2} onPress={() => resizeBox(1)}>
<Text style={style.subtext}>Add <Text style={{ color: layouts.secondary }}>+</Text></Text>
</TouchableOpacity>
<Modal transparent visible={visible}>
<SafeAreaView style={{ flex: 1 }} onTouchStart={() => resizeBox(0)}>
<Animated.View
style={[
style.popUp,
{ opacity: scale.interpolate({ inputRange: [0, 1], outputRange:[0, 1]}) },
{ transform: [{ scale }]},
]}
>
{/* THERE IS THE CODE WHERE IT DONT WORK
=>>>*/}
{actions.map((op, i) => (
<TouchableOpacity style={[
style.option,
{ borderBottomWidth: i === actions.length - 1 ? 0 : 1 }]}
key={i}
onPress={() => navigation.navigate('CreateReaction')}
>
<Text style={{ fontFamily: "Title-Font", fontSize: 15 }}>{op.title}</Text>
</TouchableOpacity>
))}
</Animated.View>
</SafeAreaView>
</Modal>
</>
)
}
Believe navigation does not exist in your functional components because the error indicates it is undefined.
Try to import the useNavigation() hook from react-navigation library and use the returned navigate function.
import { useNavigation } from '#react-navigation/native';
function PopupMenuAction ({navigation}) {
const { navigate } = useNavigation()
return (
// rest of your code
)
docs: https://reactnavigation.org/docs/use-navigation/

react native reanimated animating text style

So, I was getting familiar with reanimated2, for that I'm building a simple todo app, so whenever I check a single todo (a simple toggle state, completed or !completed), I change the text style to line-through so that it appears to be completed, now in order to add some animations, I was trying to combine reanimated to it, but was unsuccessful, so can anyone guide me how to achieve what I want?
const TodoItem = ({ todo }) => {
const { toggleTodo } = useTodoStore((state) => state);
const progress = useSharedValue('none');
useEffect(() => {
progress.value = withTiming(todo.completed ? 'line-through' : 'none', {
duration: 1000,
easing: Easing.linear,
})
}, [todo.completed]);
const textReanimatedStyle = useAnimatedStyle(() => ({
textDecorationLine: progress.value, // ts error: Type 'string' is not assignable to type 'number | "none" | "underline" | "line-through" | "underline line-through" | undefined'.
}), []);
const toggleTodoHandler = () => {
toggleTodo(todo.id);
};
return (
<Animated.View
style={styles.container}
entering={FadeIn}
exiting={FadeOut}
layout={Layout.delay(300)}>
<View style={styles.innerContainer}>
<TouchableOpacity
style={styles.checkboxContainer}
activeOpacity={0.6}
onPress={toggleTodoHandler}>
{todo.completed ? (
<Ionicon name="checkmark" size={20} color={'gray'} />
) : null}
</TouchableOpacity>
<Animated.Text
numberOfLines={2}
style={[
styles.text,
textReanimatedStyle,
]}>
{todo.text}
</Animated.Text>
</View>
</Animated.View>
);
};
Now whenever I toggle complete or not, it throws error saying:
Invalid RCTTextDecorationLineType 'noneNaN'. should be one of: (
"line-through",
none,
underline,
"underline line-through"
)
Not sure this is what you wanted to achive. But what I did was can be handle with a state change also without reanimated.
There is an alternate way also. check this answer https://stackoverflow.com/a/65262789/8743951
const textState = useSharedValue('none');
const [todo, setTodo] = useState<{ text: string; completed: boolean }>({ text: 'complete this', completed: false });
const textReanimatedStyle = useAnimatedStyle(() => {
if (textState.value === 'checked') {
return {
textDecorationLine: 'line-through',
};
}
return {
textDecorationLine: 'none',
};
}, []);
const toggleTodoHandler = (): void => {
textState.value = todo.completed ? 'none' : 'checked';
setTodo({ ...todo, completed: !todo.completed });
};
return (
<Animated.View style={styles.container} entering={FadeIn} exiting={FadeOut} layout={Layout.delay(300)}>
<View style={styles.innerContainer}>
<TouchableOpacity style={styles.checkboxContainer} activeOpacity={0.6} onPress={toggleTodoHandler}>
{todo.completed ? <Ionicon name="checkmark" size={20} color="gray" /> : null}
</TouchableOpacity>
<Animated.Text numberOfLines={2} style={[styles.text, textReanimatedStyle]}>
{todo.text}
</Animated.Text>
</View>
</Animated.View>
);

Understanding UI State : Dynamic TextInput loses focus after each key press

Screens and navigating is fine, but getting data from users has been a struggle. Not only are the UI elements different, the means to capture input and store in state effectively across various input types is troubling me. Here is an example of what I think is a simple UX yet I cannot get the text inputs to focus correctly.
In the below example, the desire is to have a list of items within a horizontal scroll view and when i click on the arrow of an item, the screen scrolls and a form to edit the item appears with one or more text boxes. Future enhancements were to have this second panel have more fields based on the type of field from the list, but i cant even get a simple text box to work properly
I've got some code to boot, copy and paste as app.js in an expo init project and it should run
main question: how to retain focus on inputs on the detail panel
import React from "react";
import {
Dimensions,
FlatList,
SafeAreaView,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
const init_items = [
{ name: "start", value: 12500, type: "num" },
{ name: "end", value: 12700, type: "num" },
{ name: "time", value: 123.45, type: "time" },
];
const main_color = "#dddddd";
const _base = 3;
const _width = Dimensions.get("window").width;
export default function App() {
const [index, setIndex] = React.useState(0);
const [items, setItems] = React.useState(init_items);
const [curItem, setCurItem] = React.useState("");
const ref = React.useRef(null);
const textRef = React.useRef(null);
React.useEffect(() => {
console.log("index chsnaged?", index);
//if (!index) return;
ref.current?.scrollToIndex({ index, animated: true });
}, [index]);
React.useEffect(() => {
setIndex(curItem === "" ? 0 : 1);
}, [curItem]);
const useCurItem = () => {
if (curItem == "") return;
return items.find((item) => item.name == curItem);
};
const setCurItemValue = (value) => {
console.log("update " + curItem + " to " + value);
const new_items = items.map((item) => {
if (item.name == curItem) return { ...item, value: value };
return item;
});
console.log("new_items: ", new_items);
setItems(new_items);
};
const Button = ({ type, press }) => {
return (
<TouchableOpacity onPress={() => press(type)}>
<Text style={{ fontSize: 20, fontWeight: "900", margin: _base }}>
{type == "arrow" ? ">" : "X"}
</Text>
</TouchableOpacity>
);
};
const ListPanel = () => {
return (
<View>
{items.map((item) => {
return (
<View
key={item.name}
style={{
margin: _base,
}}
>
<Text style={{ fontWeight: "600", margin: _base }}>
{item.name}
</Text>
<View
style={{
alignItems: "center",
backgroundColor: "white",
borderRadius: _base,
flexDirection: "row",
justifyContent: "space-between",
margin: _base,
padding: _base,
}}
>
<Text>{item.value}</Text>
<Button type="arrow" press={() => setCurItem(item.name)} />
{/* <EmojiButton
name={"fat_arrow"}
onPress={() => setCurItem(item.name)}
size={20}
/> */}
</View>
</View>
);
})}
</View>
);
};
const DetailPanel = () => {
let thisItem = useCurItem();
if (!thisItem) return null;
return (
<View style={{ width: "100%" }}>
{/* <EmojiButton name="arrow_left" onPress={() => setCurItem("")} /> */}
<Button type="cancel" press={() => setCurItem("")} />
<Text>{curItem}</Text>
<Text>{thisItem?.value}</Text>
<Text>{thisItem.type}</Text>
{thisItem.type == "num" && (
<TextInput
ref={textRef}
onChangeText={(text) => setCurItemValue(text)}
// onSubmitEditing={() => textRef.current.focus()}
style={{ backgroundColor: "white", margin: 2 }}
value={thisItem.value.toString()}
/>
)}
</View>
);
};
const screens = [
{ name: "listing", panel: <ListPanel /> },
{ name: "detail", panel: <DetailPanel /> },
];
return (
<View style={{ marginTop: 30 }}>
<Text>Sample sliding inputs</Text>
<FlatList
bounces={false}
horizontal
keyExtractor={(item) => item.name}
ref={ref}
showsHorizontalScrollIndicator={false}
data={screens}
renderItem={({ item, index: fIndex }) => {
console.log("rendering " + item);
return (
<View
style={{
backgroundColor: main_color,
height: 300,
width: _width,
padding: _base,
}}
>
<Text> {item.name}</Text>
{item.panel}
</View>
);
}}
/>
<Text>index: {index}</Text>
<Text>curItem: {curItem}</Text>
<TouchableOpacity onPress={() => setCurItem("")}>
<View>
<Text>reset</Text>
</View>
</TouchableOpacity>
</View>
);
}

Why is AsyncStorage not retrieving data once I refresh my App?

I am building a todo app and I am trying to store and retrieve data but it's not retrieving any data that is being stored. Once I refresh the data doesn't seem to persist. If there is another way of storing or writing my code please assist. I tried using other methods of storage like MMKV but it was just similar to AsyncStorage so I decided to stick with AsyncStorage. Here is my code:
import AsyncStorage from "#react-native-async-storage/async-storage";
export default function todaytodo() {
const [modalOpen, setModalOpen] = useState(false);
const [todos, setTodos] = useState("");
const storedata = async () => {
try {
await AsyncStorage.setItem("Todos", JSON.stringify(todos));
} catch (err) {
console.log(err);
}
};
const loadData = async () => {
try {
const value = await AsyncStorage.getItem("Todos");
if (value !== null) {
console.log(value);
return value;
}
} catch (error) {
console.log(error);
}
};
useEffect(() => {
storedata();
loadData();
});
const toggleComplete = (index) =>
setTodos(
todos.map((Todo, k) =>
k === index ? { ...Todo, complete: !Todo.complete } : Todo
)
);
const pressHandler = (key) => {
setTodos((prevTodos) => {
return prevTodos.filter((todo) => todo.key != key);
});
};
const submitHandler = (Todo) => {
Todo.key = Math.random().toString();
setTodos((currentTodo) => {
return [Todo, ...currentTodo];
});
setModalOpen(false);
};
return (
<View style={styles.container}>
<View>
<View>
<Ionicons
style={{
position: "absolute",
marginTop: 650,
alignSelf: "flex-end",
zIndex: 10,
marginRight: 5,
}}
name="md-add-circle-outline"
size={73}
color="black"
onPress={() => setModalOpen(true)}
/>
</View>
<FlatList
data={todos}
renderItem={({ item, index, complete }) => (
<TouchableOpacity onPress={() => toggleComplete(index)}>
<ScrollView>
<View style={styles.everything}>
<View style={styles.itemlist}>
<Checkbox
label="delete"
checked={true}
onPress={() => pressHandler(item.key)}
/>
<Text
style={{
marginLeft: 8,
marginTop: 5,
fontSize: 15,
textDecorationLine: item.complete
? "line-through"
: "none",
color: item.complete ? "#a9a9a9" : "black",
}}
>
{item.Todo}
</Text>
</View>
<Text
style={{
fontSize: 12,
marginLeft: 50,
marginTop: -15,
color: "#008b8b",
textDecorationLine: item.complete
? "line-through"
: "none",
color: item.complete ? "#a9a9a9" : "#008b8b",
}}
>
{item.Comment}
</Text>
</View>
</ScrollView>
</TouchableOpacity>
)}
/>
</View>
<View style={styles.modalcont}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<RNModal visible={modalOpen} animationType="slide">
<View style={styles.modalContent}>
<Ionicons
name="md-close-circle-outline"
style={{ alignSelf: "center" }}
size={60}
color="black"
onPress={() => setModalOpen(false)}
/>
<AddForm submitHandler={submitHandler} />
</View>
</RNModal>
</TouchableWithoutFeedback>
</View>
</View>
);
}
Use of useEffect is suspicious here, If you want to do it once on load of component
then need to update code for useEffect.
useEffect(() => {
storedata();
loadData();
}, []);

Possible Unhandled Promise Rejection (id: 0): TypeError: undefined is not an object (evaluating '_this.props.navigation.navigate')

I'm trying to display an image I have captured using expo-camera, from the camera component I'm trying to navigate to a new file which will display the image but after I took the image it won't navigate to the new page.
I tried importing the file and then navigate it but it still won't work and give me the warning instead.
This is the code where I tried to navigate to the new file.
export default class CameraExample extends React.Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' });
}
snap = async() => {
if(this.camera) {
console.log('Taking photo');
const options = {quality: 1, base64: true, fixOrientation: true, exif: true};
const photo = await this.camera.takePictureAsync(options);
this.props.navigation.navigate("Show", {photouri: photo.uri})
}
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={this.state.type}
ref = {ref => {
this.camera = ref;
}}
>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity onPress={this.snap.bind(this)}>
<Ionicons
name = "md-camera"
color = "white"
size = {30}
/>
</TouchableOpacity>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type:
this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back,
});
}}>
<Ionicons
name = "md-reverse-camera"
color = "white"
size = {30}
/>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
}
}
And this is the code where I try to display the image.
export default class ShowImages extends React.Component{
render(){
console.log('OK')
const { navigation } = this.props;
const paramm = navigation.getParam('photouri');
return(
<Content>
<View>
<Text>
paramm: {JSON.stringify(paramm)}
</Text>
<Image style={{height: 700, width: 850, alignSelf: "center"}}
source={{uri: this.props.navigation.state.paramm.photouri}}
resizeMode="contain"/>
</View>
</Content>
)
}
}
I expect it to navigate to the new page and display the captured
image but it gave me the warning. I can't seem to find what is wrong with my code. Can anyone suggest what I should do? Thank you.
change this
<TouchableOpacity onPress={this.snap.bind(this)}> => <TouchableOpacity onPress={this.snap}>
Put it in the status value and pass it on.
export default class ShowImages extends React.Component{
constructor(props) {
super(props);
this.state = {
paramm: this.props.navigation.state.params.photouri
};
}
...
<Image style={{height: 700, width: 850, alignSelf: "center"}}
source={{uri: this.state.paramm }}
resizeMode="contain"/>