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
So i want to make a list with a heart button
when i tap on one heart button.
i want only one button changed color.
and save the current button color state.
My Problem is
When i tap on one heart button, all button changed color.
Screenshot:
this is my code
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Button,
} from 'react-native';
import { ListItem } from 'react-native-elements'
import { Icon } from 'react-native-elements'
import Data from './src/data/sampledata.json';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
buttonColor: '#979797', // default button color goes here, grey is default
};
}
onButtonPress = () => {
if(this.state.buttonColor=='#ff002b')
{
this.setState({ buttonColor: '#979797' }) // grey
}
else {
this.setState({ buttonColor: '#ff002b' }) // red
}
}
render() {
return (
<View style={styles.container}>
{
Data.map((item, i) => (
<ListItem
key={item.index}
title={item.dataItem}
rightIcon={
<Icon
raised
name='heart'
type='font-awesome'
color={this.state.buttonColor}
onPress={this.onButtonPress}
/>
}
/>
))
}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
});
One way to achieve this is as follows:
What you can do is keep track of the selected items in an array. Using getUpdatedSelectedItemsArray function you can get an array that contains all the selected item like so
const getUpdatedSelectedItemsArray = (selectedItems, id) => {
const forDeletion = [id]; //can also be used for multiple ids
if (selectedItems.includes(id)) {
//if id already exists delete it
return selectedItems.filter(item => !forDeletion.includes(item));
}
//otherwise push it
selectedItems.push(id);
return selectedItems;
};
you can call the above function from this.onButtonPress(item) which takes item as an argument.
//Creating state
this.state = {
buttonColor: '#979797',
selectedItems: []
};
Calling getUpdatedSelectedItemsArray() will give you updated array which you can set in your selectedItems state.
Lastly you will have check while giving the color to icon(item), that if item.id exists in this.state.selectedItems then show red otherwise grey, like so
Data.map((item, i) => (
<ListItem
key={item.index}
title={item.dataItem}
rightIcon={
<Icon
raised
name='heart'
type='font-awesome'
color={this.checkIfIDExists(item.id) ? 'red' : 'grey'}
onPress={this.onButtonPress}
/>
}
/>
))
P.S this.checkIfIDExists(item.id) is a function the returns true if id exists in selectedItems array.
Why are my random colors being updated multiple times? Is the right way to control this behavior through a lifecycle event?
import React from 'react';
import { StyleSheet, Text, ScrollView, FlatList, SectionList, View, Button, SegmentedControlIOS } from 'react-native';
import contacts, {compareNames} from './contacts';
import {Constants} from 'expo';
import PropTypes from 'prop-types'
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
const Row=(props)=>(
<View style={styles.row}>
<Text style={{color:props.color}} >{props.name}</Text>
<Text >{props.phone}</Text>
</View>
)
const renderItem=(obj)=> {
return(<Row {...(obj.item)} color={getRandomColor()} />)
}
const ContactsList = props => {
const renderSectionHeader=(obj) =><Text>{obj.section.title}</Text>
const contactsByLetter = props.contacts.reduce((obj, contact) =>{
const firstLetter = contact.name[0].toUpperCase()
return{
...obj,
[firstLetter]: [...(obj[firstLetter] || []),contact],
}
},{})
const sections = Object.keys(contactsByLetter).sort().map(letter=>({
title: letter,
data: contactsByLetter[letter],
}))
return(
<SectionList
keyExtractor = { (item, key) => key.toString() }
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
sections={sections}
/>
)}
ContactsList.propTypes ={
renderItem: PropTypes.func,
renderSectionHeader: PropTypes.func,
contacts: PropTypes.array,
sections: PropTypes.func
}
export default class App extends React.Component {
state={show: false, selectedIndex: 0, contacts: contacts}
toggleContacts=()=>{
this.setState({show:!this.state.show})
}
sort=()=>{
this.setState({contacts: [...this.state.contacts].sort(compareNames)})
}
render() {
return (
<View style={styles.container}>
<Button title="toggle names" onPress={this.toggleContacts} />
<Button title="sort" onPress={this.sort} />
<SegmentedControlIOS
values={['ScrollView', 'FlatList','SectionList']}
selectedIndex={this.state.selectedIndex}
onChange={(event) => {
this.setState({selectedIndex: event.nativeEvent.selectedSegmentIndex});
}} />
{this.state.show && this.state.selectedIndex === 0 &&
<ScrollView >
{this.state.contacts.map(contact=>(
<Row {...contact}/> ))}
</ScrollView>}
{this.state.show && this.state.selectedIndex === 1 &&
<FlatList
data={this.state.contacts}
keyExtractor = { (item, index) => index.toString() }
renderItem={renderItem}>
</FlatList>}
{this.state.show && this.state.selectedIndex === 2 &&
<ContactsList
contacts={this.state.contacts}>
</ContactsList>}
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
// alignItems: 'flex-start',
paddingTop: Constants.statusBarHeight + 25,
},
row: {
padding:20,
},
});
const NUM_CONTACTS = 10
const firstNames = ['Emma','Noah','Olivia','Liam','Ava','William','Sophia','Mason','Isabella','James','Mia','Benjamin','Charlotte','Jacob','Abigail','Michael','Emily','Elijah','Harper','Ethan','Amelia','Alexander','Evelyn','Oliver','Elizabeth','Daniel','Sofia','Lucas','Madison','Matthew','Avery','Aiden','Ella','Jackson','Scarlett','Logan','Grace','David','Chloe','Joseph','Victoria','Samuel','Riley','Henry','Aria','Owen','Lily','Sebastian','Aubrey','Gabriel','Zoey','Carter','Penelope','Jayden','Lillian','John','Addison','Luke','Layla','Anthony','Natalie','Isaac','Camila','Dylan','Hannah','Wyatt','Brooklyn','Andrew','Zoe','Joshua','Nora','Christopher','Leah','Grayson','Savannah','Jack','Audrey','Julian','Claire','Ryan','Eleanor','Jaxon','Skylar','Levi','Ellie','Nathan','Samantha','Caleb','Stella','Hunter','Paisley','Christian','Violet','Isaiah','Mila','Thomas','Allison','Aaron','Alexa','Lincoln']
const lastNames = ['Smith','Jones','Brown','Johnson','Williams','Miller','Taylor','Wilson','Davis','White','Clark','Hall','Thomas','Thompson','Moore','Hill','Walker','Anderson','Wright','Martin','Wood','Allen','Robinson','Lewis','Scott','Young','Jackson','Adams','Tryniski','Green','Evans','King','Baker','John','Harris','Roberts','Campbell','James','Stewart','Lee','County','Turner','Parker','Cook','Mc','Edwards','Morris','Mitchell','Bell','Ward','Watson','Morgan','Davies','Cooper','Phillips','Rogers','Gray','Hughes','Harrison','Carter','Murphy']
// generate a random number between min and max
const rand = (max, min = 0) => Math.floor(Math.random() * (max - min + 1)) + min
// generate a name
const generateName = () => `${firstNames[rand(firstNames.length - 1)]} ${lastNames[rand(lastNames.length - 1)]}`
// generate a phone number
const generatePhoneNumber = () => `${rand(999, 100)}-${rand(999, 100)}-${rand(9999, 1000)}`
// create a person
const createContact = () => ({name: generateName(), phone: generatePhoneNumber()})
// compare two contacts for alphabetizing
export const compareNames = (contact1, contact2) => contact1.name > contact2.name
// add keys to based on index
const addKeys = (val, key) => ({key, ...val})
// create an array of length NUM_CONTACTS and alphabetize by name
export default Array.from({length: NUM_CONTACTS}, createContact).map(addKeys)
It looks like your component is triggering the render method multiple times. This happens mostly when you use setState method, since everytime your state changes the render method is triggered.
You may have 2 options to handle this:
1) Identify where your component is being rerendered and treat it if this behavior is unnecessary. You can use console.log to debug it.
2) You can avoid the random method to be called multiple times if you call it only in your componentDidMount method. Since componentDidMount is called only once in the component lifecycle, all functions inside of it will not be triggered when the component rerenders, only when it mount again. (I think this is the solution)
Let me know if it helps.
I just began playing around with React Native and I was trying to figure out how to disable and enable a touchableOpacity when the lengths of two inputTexts are > 0.
On my touchableOpacity I have it set to disabled={true} and it has a children Text with opacity set. When a user types into the textInput's I want to set disabled={false} and change the Text opacity to 1.
What's the best way to do this in react native?
Should i put ref's on them?
Should I use a className?
setNativeProps maybe?
If your constructor's state contains a flag for "isDisabled" and "textOpacity", then you can call setState in the onChangeText function that will change the state of isDisabled and textOpacity. The text component can use the opacity from the state's textOpacity and the touchableOpacity can use the state's isDisabled.
Ex:
`class foo extends Component {
constructor(props) {
super(props);
this.state = {
isDisabled: true,
textOpacity: 0.1,
text1: '',
text2: '',
};
onTextChange1(text) {
if (text.length > 0 && this.state.text2.length > 0) {
this.setState({isDisabled: false, textOpacity: 1, text1: text});
}
else {
this.setState({isDisabled: true, textOpacity: 0.1, text1: text});
}
}
onTextChange2(text) {
if (text.length > 0 && this.state.text1.length > 0) {
this.setState({isDisabled: false, textOpacity: 1, text2: text});
}
else {
this.setState({isDisabled: true, textOpacity: 0.1, text2: text});
}
}
render() {
return (
<View>
<TextInput onChangeText={(text) => this.onTextChange1(text)} value={this.state.text1}/>
<TextInput onChangeText={(text) => this.onTextChange2(text)} value={this.state.text2}/>
<TouchableOpacity disabled={this.state.isDisabled}>
<Text style={{opacity:this.state.textOpacity}}>Hello World!</Text>
</TouchableOpacity>
</View>
);
}
}
I have not tested the above, but I believe this is basically what you are looking for. We perform a check in the on text change to see if a certain condition is met, and depending on if it is, we change the parameters of the child components as you stated. Calling setState and modifying props are the only ways to trigger a rerender, so this is how in react-native we work with rerendering components.
if you want your opacity to be able to change depending on user moves, you need to set it(opacity) in state of your parent component.You can do this for example:
export class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { opacity: 0 }
}
render() {
Some components ....
<TextInput style={{opacity: this.state.opacity}} onChangeText={ () => this.setState({opacity: 1}) } ...other props here .../>
Some components ....
}
}
you can also apply other styles to your TextInput component.
I am looking for a text editor component for React Native without WebView. I found only react-native-zss-rich-text-editor, but it uses WebView, which I think is terrible.
I hope to find something, that works with NSAttributedString and SpannableString for IOS and Android in a native way, like in Evernote, for example.
Evernote Android app text editor
Here is a TextArea component, that wraps TextInput and resizes it automatically, when you press "new line" button
import React, { PropTypes } from 'react'
import { TextInput } from 'react-native'
export default class TextArea extends React.Component {
static propTypes = {
text: PropTypes.string.isRequired,
onChangeText: PropTypes.func.isRequired,
initialHeight: PropTypes.number,
isEditing: PropTypes.bool,
scrollViewRef: PropTypes.object,
}
static defaultProps = {
initialHeight: 40,
isEditing: true,
scrollViewRef: null,
}
state = {
height: this.props.initialHeight,
}
componentWillUnmount(){
this._isUnmounted = false
}
focus(){
this.refs.textInput.focus()
}
blur(){
this.refs.textInput.blur()
}
_contentSizeChanged = e => {
this.setState({
height: e.nativeEvent.contentSize.height,
}, () => {
if (this.props.scrollViewRef) {
setTimeout(() => {
if (!this._isUnmounted) this.props.scrollViewRef.scrollToEnd()
}, 0)
}
})
}
_onChangeText = text => {
this.props.onChangeText(text)
}
render(){
return (
<TextInput
ref = "textInput"
multiline
value = {this.props.text}
style = {{ height: this.state.height, color: 'black', flex: 1 }}
onChangeText = {this._onChangeText}
onContentSizeChange = {this._contentSizeChanged}
editable = {this.props.isEditing}
blurOnSubmit = {false}
/>
)
}
}