In React Native, creating a canva and calling getcontext create error - react-native

I am trying to use a function called readpixels from this github page and in this function one need to get the context of a canva, but since I am using React Native, I cannot use expressions like new Image() or document.createElement('canvas') so I am trying to do an equivalent using React Native functions.
Here is a minimal version of the code:
import React, { useState, useEffect, useRef } from 'react';
import { Button, Image, View } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Canvas from 'react-native-canvas';
export function Canva() {
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
const ctx = ref.current.getContext('2d');
if (ctx) {
Alert.alert('Canvas is ready');
}
}
}, [ref]);
return (
<Canvas ref={ref} />
);
}
function readpixels(url, limit = 0) {
const img = React.createElement(
"img",
{
src: url,
},
)
const canvas = Canva()
const ctx = canvas.getContext('2d')
return 1
}
export default function ImagePickerExample() {
const [image, setImage] = useState(null);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
quality: 1,
});
readpixels(result.uri)
if (!result.cancelled) {
setImage({uri: result.uri, fileSize: result.fileSize});
}
};
return (
<View style={{ flex: 1, backgroundColor: "white", marginTop: 50 }} >
<Button title="Pick image from camera roll" onPress={pickImage} />
{image && <Image source={{ uri: image.uri }} style={{ width: 200, height: 200 }} />}
</View>
);
}
And here is the error that I get:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I have checked the three suggestions to solve the issue, but it did not work.
Thank you
p.s: in order to reproduce the code, you would need to install the react-native-canvas package

Related

Warning: React has detected a change in the order of Hooks

I have run into this error in my code, and don't really know how to solve it, can anyone help me?
I get the following error message:
ERROR Warning: React has detected a change in the order of Hooks called by ScreenA. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks
import React, { useCallback, useEffect, useState } from "react";
import { View, Text, StyleSheet, Pressable } from "react-native";
import { useNavigation } from '#react-navigation/native';
import { DancingScript_400Regular } from "#expo-google-fonts/dancing-script";
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
export default function ScreenA({ route }) {
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
// Keep the splash screen visible while we fetch resources
await SplashScreen.preventAutoHideAsync();
// Pre-load fonts, make any API calls you need to do here
await Font.loadAsync({ DancingScript_400Regular });
// Artificially delay for two seconds to simulate a slow loading
// experience. Please remove this if you copy and paste the code!
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (e) {
console.warn(e);
} finally {
// Tell the application to render
setAppIsReady(true);
}
}
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
// This tells the splash screen to hide immediately! If we call this after
// `setAppIsReady`, then we may see a blank screen while the app is
// loading its initial state and rendering its first pixels. So instead,
// we hide the splash screen once we know the root view has already
// performed layout.
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
const navigation = useNavigation();
const onPressHandler = () => {
// navigation.navigate('Screen_B', { itemName: 'Item from Screen A', itemID: 12 });
}
return (
<View style={styles.body} onLayout={onLayoutRootView}>
<Text style={styles.text}>
Screen A
</Text>
<Pressable
onPress={onPressHandler}
style={({ pressed }) => ({ backgroundColor: pressed ? '#ddd' : '#0f0' })}
>
<Text style={styles.text}>
Go To Screen B
</Text>
</Pressable>
<Text style={styles.text}>{route.params?.Message}</Text>
</View>
)
}
const styles = StyleSheet.create({
body: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 40,
margin: 10,
fontFamily: 'DancingScript_400Regular'
}
})
I have read the rules of hooks: https://reactjs.org/docs/hooks-rules.html
The output is correct, but i want to fix this error before i add more additions to the app
You need to move useNavigation use before early returns.
Instead, always use Hooks at the top level of your React function, before any early returns.
The key is you need to call all the hooks in the exact same order on every component lifecycle update, which means you can't use hooks with conditional operators or loop statements such as:
if (customValue) useHook();
// or
for (let i = 0; i< customValue; i++) useHook();
// or
if (customValue) return;
useHook();
So moving const navigation = useNavigation(); before if (!appIsReady) {return null;}, should solve your problem:
export default function ScreenA({ route }) {
const [appIsReady, setAppIsReady] = useState(false);
const navigation = useNavigation();
// ...
}

React Native token issue when submit

i build an application with reactNative and Expo for front-end and django for back-end
i want take token from AsyncStorage to recognised the user, but the is an errors
Warning: An unhandled error was caught from submitForm(), [ReferenceError: Can't find variable: useState]
when i change the owner id to static value the submit works fine and show the token value on the console log not sure where is the issue exactly
import React, {useState} from 'react';
import { FieldArray, Formik } from "formik";
import axios from "axios";
import AsyncStorage from '#react-native-async-storage/async-storage';
import jwt_decode from "jwt-decode";
function AddScreen(props){
const submit = ({ type, category, amount }) => {
//here you define the token to be set
const [token, setToken] = useState();
//here you get the token from AsyncStorage
const getToken = async () => {
try {
const data = await AsyncStorage.getItem('token');
if (data !== null) {
setToken(data)
return data;
}
} catch (error) {
console.log(error);
}
};
//here you set the token
getToken()
//here you decode the token
const tokens = token;
const decoded = jwtDecode(tokens);
//here you get the id and it returns a number
const id = decoded.user_id
const config = {
headers: {
"Content-Type": "application/json",
},
};
const date = Date.now();
const owner = id;
const body = JSON.stringify({ type, category, amount, date, owner });
if (type == "" || category == "" || amount == "") {
alert("Please Enter all details");
return;
}
// 'http://192.168.8.143:8000/transactions/'
axios
.post("http://192.168.8.143:8000/transactions/", body, config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
};
return (
<View style={{ flex: 1, backgroundColor: COLORS.gray }}>
<Formik
initialValues={{ type: "", category: "", amount: "" }}
onSubmit={(values) => {
submit(values);
}}
>
{(props) => (
<View style={styles.whitecard}>
<TextInput
style={styles.inputstyle}
placeholder="type"
onChangeText={props.handleChange("type")}
value={props.values.type}
/>
<TextInput
style={styles.inputstyle}
placeholder="category"
onChangeText={props.handleChange("category")}
value={props.values.category}
/>
<TextInput
style={styles.inputstyle}
placeholder="amount"
onChangeText={props.handleChange("amount")}
value={props.values.amount}
keyboardType="numeric"
/>
<View
style={{
margin: 10,
padding: 0,
backgroundColor: COLORS.pink,
borderRadius: 6,
}}
>
<Button
title="Submit"
color="white"
onPress={props.handleSubmit}
></Button>
</View>
</View>
)}
</Formik>
</View>
);
};
...
export default AddScreen;
the error show
Warning: An unhandled error was caught from submitForm(), [Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.]
You are not importing UseState from React. Also you need to convert your code into a react functional component.
EDIT
For your new error, you need to move the useState out of submit. It must be at the root level of the functional component. I am updating the code example below as well.
import React, {useState} from 'react';
//All your other imports
function AddScreen(props) {
const [token, setToken] = useState(null);
//Everything else remains same inside
};

Does react-native supports Pintura Editor?

I want to use Pintura Editor in my react-native project, But it's not working and also didn't getting any error.
I am not able to understand where i am going wrong and does react-native supports Pintura ?
Is there any way i can solve this issue?
Any lead would be highly appreciated. Thank you in advance.
React-Native doesn't supports Pintura Editor yet. But we can use pintura with WebView, as Pintura is based on html technologies it will have to run in a WebView to work. Here is an example
Pintura_Editor.js
import { WebView } from 'react-native-webview';
import { View } from 'react-native';
import React, { forwardRef, useRef, useEffect,useState } from 'react';
const noop = () => {};
//For Making First letter uppercase.
const upperCaseFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1);
// PATH_TO_PINTURA.HTML file (need to download paid version)
const PinturaEditorProxy = require('./pintura.html');
const Editor = forwardRef((props, ref) => {
const { style, ...options } = props;
const webViewRef = useRef(null);
const [isStatus, setStatus] = useState(false)
setInterval(()=> {
setStatus(true)
}, 3000)
// this passes options to the editor
useEffect(() => {
clearInterval();
// setup proxy to call functions on editor
const handler = {
get: (target, prop) => {
if (prop === 'history') return new Proxy({ group: 'history' }, handler);
return (...args) => {
const name = [target.group, prop].filter(Boolean).join('.');
webViewRef.current.postMessage(
JSON.stringify({
fn: [name, ...args],
})
);
};
},
};
webViewRef.current.editor = new Proxy({}, handler);
// post new options
webViewRef.current.postMessage(JSON.stringify({ options: options }));
},[isStatus]);
return (
<View ref={ref} style={style}>
<WebView
ref={webViewRef}
javaScriptEnabled={true}
scrollEnabled={false}
domStorageEnabled={true}
allowFileAccess={true}
allowUniversalAccessFromFileURLs={true}
allowsLinkPreview={false}
automaticallyAdjustContentInsets={false}
originWhitelist={['*']}
textZoom={100}
source={PinturaEditorProxy}
onMessage={async (event) => {
// message from WebView
const { type, detail } = JSON.parse(event.nativeEvent.data);
const handler = await options[`on${upperCaseFirstLetter(type)}`] || noop;
// call handler
handler(detail);
}}
/>
</View>
);
});
export default Editor;
Now, import this editor to your app.js
import PinturaEditor from 'PATH_TO_PINTURA_EDITOR'
const editorRef = null;
....
....
render() {
return (
<PinturaEditor
ref={editorRef}
style={{ margin:40, width: '80%', height: '80%', borderWidth: 2, borderColor: '#eee' }}
imageCropAspectRatio={1}
// Image should be base64 or blob type
src={base64Image}
onLoad={(img) => {
// do something
}}
onProcess={async (image) => {
// do something with edited image
}}
/>)
}

React Native how to get the local time format of device?

I need to get the time format from local device . And I have tried this "https://www.npmjs.com/package/react-native-device-time-format?activeTab=readme"
And also install the moment API and follow what the document said ,however ,when I tried ,it got an error like "
TypeError: null is not an object (evaluating 'RNDeviceTimeFormat.is24HourFormat')
"
I attached my code ,could you please help me to take a look ? Thank you so much !!
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { is24HourFormat } from 'react-native-device-time-format';
import moment from 'moment'
function TryTime(props) {
const getCurrentHourFormat = async (date) => {
const is24Hour = await is24HourFormat();
return (moment(date).format(is24Hour ? 'HH:mm' : 'h:mm A'));
}
return (
<View style={styles.container}>
<Text>{is24Hour}</Text>
</View>
);
}
const styles = StyleSheet.create({
container : {
flex : 1,
justifyContent : 'center',
alignItems : 'center',
}
})
export default TryTime;
There are a few things wrong with your code: you defined a function getCurrentHourFormat but haven't run it yet, so is24Hour is undefined.
function TryTime(props) {
const [currentTime, setCurrentTime] = useState("");
const getCurrentHourFormat = async (date) => {
const is24Hour = await is24HourFormat();
return (moment(date).format(is24Hour ? 'HH:mm' : 'h:mm A'));
}
useEffect(() => {
(async () => {
const timeNow = await TryTime(Date.now);
setCurrentTime(timeNow);
})();
}, []);
return (
<View style={styles.container}>
<Text>{currentTime}</Text>
</View>
);
}
You might want to check useEffect and how functional component work though

React Native onLayout with React Hooks

I want to measure the size of a React Native View every time it renders, and save it to state. If element layout didn't change the effect should not run.
It's easy to do with a class based component, where onLayout can be used. But what do I do in a functional component where I use React Hooks?
I've read about useLayoutEffect. If that's the way to go, do you have an example of how to use it?
I made this custom hook called useDimensions. This is how far I've got:
const useDimensions = () => {
const ref = useRef(null);
const [dimensions, setDimensions] = useState({});
useLayoutEffect(
() => {
setDimensions(/* Get size of element here? How? */);
},
[ref.current],
);
return [ref, dimensions];
};
And I use the hook and add the ref to the view that I want to measure the dimensions of.
const [ref, dimensions] = useDimensions();
return (
<View ref={ref}>
...
</View>
);
I've tried to debug ref.current but didn't find anything useful there. I've also tried measure() inside the effect hook:
ref.current.measure((size) => {
setDimensions(size); // size is always 0
});
If you could like a more self-contained version of this here is a custom hook version for React Native:
const useComponentSize = () => {
const [size, setSize] = useState(null);
const onLayout = useCallback(event => {
const { width, height } = event.nativeEvent.layout;
setSize({ width, height });
}, []);
return [size, onLayout];
};
const Component = () => {
const [size, onLayout] = useComponentSize();
return <View onLayout={onLayout} />;
};
You had the right idea, it just needed a couple of tweaks... mainly, handing in the element ref and using elementRef (not elementRef.current) in the useEffect dependency array.
(Regarding useEffect vs useLayoutEffect, as you're only measuring rather than mutating the DOM then I believe useEffect is the way to go, but you can swap it out like-for-like if you need to)
const useDimensions = elementRef => {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useEffect(() => {
const el = elementRef.current;
setDimensions({ width: el.clientWidth, height: el.clientHeight });
}, [elementRef]);
return [dimensions];
};
Use it like this:
function App() {
const divRef = useRef(null);
const [dimensions] = useDimensions(divRef);
return (
<div ref={divRef} className="App">
<div>
width: {dimensions.width}, height: {dimensions.height}
</div>
</div>
);
}
Working codesandbox here
Edited to Add React Native version:
For React Native you can use useState with onLayout like this:
const App=()=>{
const [dimensions, setDimensions] = useState({width:0, height:0})
return (
<View onLayout={(event) => {
const {x, y, width, height} = event.nativeEvent.layout;
setDimensions({width:width, height:height});
}}>
<Text}>
height: {dimensions.height} width: {dimensions.width}
</Text>
</View>
);
}
As a refinement to
matto1990's answer, and to answer Kerkness's question - here's an example custom hook that supplies the x, y position as well as the layout size:
const useComponentLayout = () => {
const [layout, setLayout] = React.useState(null);
const onLayout = React.useCallback(event => {
const layout = event.nativeEvent.layout;
setLayout(layout);
}, [])
return [layout, onLayout]
}
const Component = () => {
const [{ height, width, x, y }, onLayout] = useComponentSize();
return <View onLayout={onLayout} />;
};