How to use useEffect inside a View in React Native - reactjs

I want to use an useEffect inside a return (inside a Text that is inside multiple View to be exact) and from what I know, I must use {...} in order to say that what I write is some code. Howether I got a blank screen without errors and I don't know where is the issue with my code.
Here is the code:
const [pass, setPass] = useState(0);
...
return (
<View>
<FlatList
data={letter.description}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
if (pass >= letter.description?.length) {
useEffect(() => {
setPass((prev) => 0);
});
}
return (
<View>
<Text>
{letter.data[pass]}
{"\n"}
</Text>
<Text>
{letter.description[pass]}
{useEffect(() => {
setPass((prev) => prev + 1);
})}
{"\n"}
</Text>
</View>
);
}}
/>
</View>
letter is my data, but you can ignore it. I just keep it here to explain why I need the pass

Why use useEffect to setState?
just set the state :
if (pass >= letter.description?.length) { setPass((prev) => 0); }
You can use UseEffect to render your component when you want to render it.
More of this here : https://reactjs.org/docs/hooks-effect.html

Related

React Native rerendering UI after using array.map

I am attempting to have an icon switch its visual when clicked (like a checkbox). Normally in react native I would do something like this:
const [checkbox, setCheckbox] = React.useState(false);
...
<TouchableHighlight underlayColor="transparent" onPress={() => {setCheckbox(!setCheckbox)}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
However I have made some changes, and now I can't seem to replicate this behavior. I am using AsyncStorage class to storage and get arrays of objects for display. For simplification, in the example below I removed the storage code, and the objects each have an 'id' and an 'added' attribute, which is essentially the boolean value of the checkbox.
I am now attempting to update the icon shown to the user whenever it is pressed. I know the function is being called, but it will not update the icon. I am using array.map to create the list of icons. I created a demo here, and the code is below: https://snack.expo.dev/#figbar/array-map-icon-update
const templateObject = {
id: 0,
added: false,
};
const templateObject2 = {
id: 1,
added: true,
};
export default function App() {
const [savedNumbers, setSavedNumbers] = React.useState([]);
React.useEffect(() => {
setSavedNumbers([templateObject,templateObject2]);
}, []);
const populateSavedNumbers = () =>
savedNumbers.map((num, index) => <View key={index}>{renderPanel(num.id,num.added)}</View>);
const updateNumber = (id) => {
let capturedIndex = -1;
for(var i = 0; i < savedNumbers.length; i += 1) {
if(savedNumbers[i].id === id) {
capturedIndex = i;
break;
}
}
let _tempArray = savedNumbers;
_tempArray[capturedIndex].added = !_tempArray[capturedIndex].added;
setSavedNumbers(_tempArray);
}
const renderPanel = (id:number, added:boolean) => {
return (
<View>
<TouchableHighlight underlayColor="transparent" onPress={() => {updateNumber(id);}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
</View>
);
}
return (
<View>
<View>buttons:</View>
<View>{populateSavedNumbers()}</View>
</View>
);
}
This is a common React pitfall where things don't re-render when it seems like they should. React does shallow comparisons between new and old states to decide whether or not to trigger a re-render. This means that, when declaring a variable to simply equal a state variable which is an object or an array, a re-render is not triggered since those two variables now reference the same underlying data structure.
In this case, you are setting _tempArray to reference the array savedNumbers rather than creating a new array. Therefore, React's shallow comparison comes back as "equal", and it doesn't believe that a re-render is necessary.
To fix this, change this line:
let _tempArray = savedNumbers;
to this:
let _tempArray = [...savedNumbers];

Conditional Logic react native "Text strings must be rendered within a <Text> component."

Hi i am making an app in react native and i have a problem
let currentUserUID = firebase.auth().currentUser.uid;
const [partnership, setPartnership] = useState('');
useEffect(() => {
async function getUserInfo(){
let doc = await firebase
.firestore()
.collection('users')
.doc(currentUserUID)
.get();
let dataObj = doc.data();
setPartnership(dataObj.partnership)
}
getUserInfo();
})
console.log(partnership)
return(
<View style={{flex: 1}}>
{partnership === "Partner" || "1KPartner" || "BusinessPartner" &&
<Text>You have permission</Text>
}
<LessonHeader navigation={navigation}/>
<StyledBoxV3 style={{flex: 1}}>
<LessonMenu item={item} navigation={navigation} />
</StyledBoxV3>
</View>
)
};
const StyledBoxV3 = styled(StyledBox)`
padding: 0;
border-top-left-radius:0;
border-top-right-radius:0 ;
`
so when someone is Partner, 1KPartner or BusinessPartner i want to show or hide specific data but i get the error
"Error: Text strings must be rendered within a component."
The problem is in de conditional logic, if i delete it i dont have any problem. How can i make this work? I already tried to put a variable in my return and update it with a function in my useEffect but it doesn't work for me.
Your logic where you are comparing the value of the partnership is written wrong. I would suggest being classy and writing
{(partnership === "Partner" ||
partnership === "1KPartner" ||
partnership === "BusinessPartner") &&
<Text>You have permission</Text>}
Try updating the condition like this,
A helper function to determine the component to render based on the partnership state.
const renderPermissionText = (value: String) => {
switch (value) {
case 'Partner':
case '1KPartner':
case 'BusinessPartner':
return <Text>You have permission</Text>;
default:
return null;
}
};
Update the Layout to
...
<View style={{flex: 1}}>
{renderPermissionText(partnership)}
...
</View>

How to delete item from an array by onPress event in react

I'am making a todo app. I want to call a function on a button Press event and want to pass item id to it.
import React ,{useState} from 'react';
import { StyleSheet, Text, View, TextInput, Button, ScrollView, Alert, FlatList} from 'react-native';
export default function App() {
const [goal,setgoal] = useState('');
const [addInput, setInput] = useState([]);
const changetext= ()=>{
setInput(addInput=>[...addInput,{id: Math.random().toString(), value: goal}]);
};
const texthandler= (enteredText)=>{
setgoal(enteredText);
};
const deleteText = (e)=>{
setInput((addInput)=>addInput.filter(todo=>todo.id !=e.target.id))
}
return (
<View style={styles.screen} >
<View style={styles.InputView}>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}
style={styles.TextInputStyle}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View style={styles.recordList}>
<Text style={styles.Textoutput} id={itemData.item.id} onPress={deleteText}>{itemData.item.value}</Text>
<Button title="Delete" style={styles.DeleteButton} value={itemData} onPress={deleteText} ></Button>
</View>)}/>
</View>
);
}
here i want Delete button to remove respective element from 'addInput' list. same thing is happening by pressing Text field itself.
but here i can pass id from text field but not from button. why so?
how to get it done by using button.
also, should i use 'this' keyword? can we do it without it, because some time it looks confusing to me at initial stage.
thanks in advance
Try this:
const deleteText = (itemID)=>{
setInput(()=>addInput.filter(todo=>todo.id !=itemID))
}
return (
<View style={{marginTop:30}}>
<View>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View>
<Text id={itemData.item.id} onPress={()=>deleteText(itemData.item.id)} >{itemData.item.value}</Text>
<Button title="Delete" value={itemData} onPress={()=>deleteText(itemData.item.id)} ></Button>
</View>)}/>
</View>
Well i am not sure about the answer in terms of React-native but in React i would try something like this:-
onPress= { this.deleteText.bind(this,idx) }
If you are not passing this function as a prop, simply remove bind and call it using this.deleteText(idx)
In react/react-native, you can make a event handler method and can handle array items for example like this:
handleOnPress = value => {
let {mArray} = this.state
if(mArray.includes(value)){
let index = mArray.findIndex((item => item==value))
mArray.splice(index, 1)
}else mArray.push(value)
this.setState({mArray})
}
In Your case, have to just delete item from the array so you can just use splice method. for example:
handleOnDelete = item => {
let {mArray} = this.state;
let index = mArray.findIndex((item => item==value))
if(index > -1) mArray.splice(index, 1)
else console.log('item not found')
}
const deleteText = e => setInput(
addInput => addInput.split(addInput.findIndex(todo => todo.id == e.target.id), 1)
)

Unable to use ref to call the child method on a connect redux component

I want to call SingleCard child component methods in renderHiddenItem. I have assigned different ref name for each renderItem. But when I call this.name, it is undefined. Anything is wrong in this code? How can I achieve this?
<SwipeListView
data={this.state.listViewData}
renderItem={(data, i) => {
const name = 'childRef'+i
return (
<SingleCard
ref={component => this.name = component}
itm={data.item}
/>
);
}}
renderHiddenItem={(data, i) => {
const name = 'childRef'+i
return (
<TouchableOpacity onPress={ () => console.log(this.name)}>
<Text> h </Text>
</TouchableOpacity>
);
}
}}
/>
Update:
I want to trigger some action which is written in the singleCard component. Need to call that in renderHiddenItem.
Like this:
this.childRef0.someMethod
this.childRef1.someMethod
Instead of name you need to use the dynamic variable which can be done by using the bracket notation
<SwipeListView
data={this.state.listViewData}
renderItem={(data, i) => {
const name = 'childRef'+i
return (
<SingleCard
ref={component => this[name] = component}
itm={data.item}
/>
);
}}
renderHiddenItem={(data, i) => {
const name = 'childRef'+i
return (
<TouchableOpacity onPress={ () => console.log(this[name])}>
<Text> h </Text>
</TouchableOpacity>
);
}
}}
/>
Also when you use ref on a component which is created using an HOC for instance connect from react-redux, most of the libraries provide a method called getWrappedInstance to get the ref for the actual component instead of the connect component. You can use it like
this[name].getWrappedInstance()
but initially you need to set {withRef: true} as the fourth parameter to connect being used in SingleCard like
connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })(SingleCard);
You can read more about it here
https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options

Flatlist scroll references with getItemLayoutProp

I've got a little problem with flatlist and scroll methods.
I have flatlist with comments and if new one is added I want to scroll list to the bottom to see added comment.
Using scrollToIndex doesn't work properly, native keep showing errors due to lack of getItemLayout, and here is another problem with setting this function - every item can have different size.
scrollToEnd has some issues, sometimes it scrolls to almost bottom of the page, sometimes to headerComponent elements which are set in flatlist props.
Do you have any ideas how to make it scroll to the bottom?
To use scrollToIndex you need to use getItemLayout. There is no point in using it if you have no intention to use getItemLayout. Here is an example taken from the react-native docs:
class ScrollToExample extends Component {
getItemLayout = (data, index) => (
{ length: 50, offset: 50 * index, index }
)
scrollToIndex = () => {
let randomIndex = Math.floor(Math.random(Date.now()) *
this.props.data.length);
this.flatListRef.scrollToIndex({animated: true, index:
randomIndex});
}
scrollToItem = () => {
let randomIndex = Math.floor(Math.random(Date.now()) *
this.props.data.length);
this.flatListRef.scrollToIndex({animated: true, index: "" +
randomIndex});
}
render() {
return (
<FlatList
style={{ flex: 1 }}
ref={(ref) => { this.flatListRef = ref; }}
keyExtractor={item => item}
getItemLayout={this.getItemLayout}
initialScrollIndex={50}
initialNumToRender={2}
renderItem={({ item, index}) => (
<View style={{...style, backgroundColor: this.getColor(index)}}>
<Text>{item}</Text>
</View>
)}
{...this.props}
/>
);
}
}
https://gist.github.com/joshyhargreaves/b8eb67d24ce58a6d8bffb469f7eeaf39
Hope this helps!

Resources