Conditional statement inside JSX in react native? - javascript

I want to set my state inside JSX expression and show my component if the condition is true. How can i achieve that? i tried this this first :
{(currenMonth !== item.orderDate)
&& (setCurrentMonth(item.orderDate) && <Item name={getMonthFromString(item.orderDate)} active />)
}
In a second solution i've created this function :
const ProductsList = () => {
const [currenMonth, setCurrenMonth] = useState('');
const renderItem = (month) => {
if (currenMonth !== month) {
setCurrenMonth(month);
return <Item name={getMonthFromString(month)} active />;
}
return null;
};
return(
<View style={styles.container}>
<FlatList
data={products}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<View>
{ renderItem(item.orderDate) }
</View>
);
}}
/>
</View>
);
}
But i'm getting an Error [Unhandled promise rejection: Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.]

There are two ways: practically the way you're doing it inside JSX and then a separate rendering function. I'll recommend the latter. But mentioned, render is not part of setting state. So you actually have two separate problems.
...
const renderMonthItem = () => (
(<yourConditionals> ? <Item ... /> : null;
)
...
return (
<View> ...
{ renderMonthItem() }
... </View>
);

Related

How to render component via FlatList?

Using react native with typescript and redux toolkit
Hi I'm bothering with render a list of messages via FlatList. By ScrollView everything rendering good but I need to implement infiniti scroll. So I'm doing something like this
const MessagesScreen = () => {
const companyId = useAppSelector(getCompanyId);
const userId = useAppSelector(getUserId);
const {
data: messages,
isLoading,
refetch
} = useGetMessagesQuery({ userId, companyId });
useFocusEffect(refetch);
return (
<FlatList
data={messages}
renderItem={() => {
<Messages messages={messages} />;
}}
/>
);
};
In return() I'm trying to render FlatList with component Messages which is down here:
const Messages = ({ messages }: { messages: Message[] }) => {
const navigation =
useNavigation<RootStackScreenProps<'DrawerNavigator'>['navigation']>();
const { colors } = useTheme();
return (
<View style={styles.container}>
{messages.map(message => {
const createdAt = message.created_at;
const isRead = message.read;
const icon = isRead ? 'email-open-outline' : 'email-outline';
const onClick = () => {
navigation.navigate('Message', {
messageId: message.id
});
};
return (
<TouchableOpacity key={message.id} onPress={onClick}>
<View
style={[styles.message, { borderBottomColor: colors.separator }]}
>
<View style={styles.iconPart}>
<Icon
name={icon}
type="material-community"
style={
isRead
? { color: colors.separator }
: { color: colors.inputFocus }
}
size={24}
></Icon>
</View>
<View style={styles.bodyPart}>
<Text
numberOfLines={1}
style={[isRead ? styles.readSubject : styles.unReadSubject]}
>
{message.subject}
</Text>
<Text
numberOfLines={1}
style={[isRead ? styles.readBody : styles.unReadBody]}
>
{message.body}
</Text>
</View>
<View style={styles.datePart}>
<Text style={{ color: colors.shadow }}>
{dayjs(createdAt).fromNow()}
</Text>
</View>
</View>
</TouchableOpacity>
);
})}
</View>
);
};
Actually behaviour is just rendering white screen with error
Possible Unhandled Promise Rejection (id: 17):
Error: Objects are not valid as a React child (found: object with keys {id, msg_type, created_at, subject, body, author, company_id, read}). If you meant to render a collection of children, use an array instead.
there is problem with your call back function:
you are not returning Messages component
1:Remove curly braces
return (
<FlatList
data={messages}
renderItem={() => <Messages messages={messages}/> }
/>
);
2:Add return statement
return (
<FlatList
data={messages}
renderItem={() => {
return <Messages messages={messages} />;
}}
/>
);
Couple things:
You're using the renderItem callback incorrectly:
<FlatList
data={messages}
renderItem={() => {
// ^ ignoring the renderItem props
return <Messages messages={messages} />;
}}
/>
Here, for each item in the messages array, you're rendering a component and passing all the messages into it. So you'll get repeated elements.
The renderItem callback is passed {item, index} where item is the CURRENT item in the array (index is the index into the array)
See docs here:
https://reactnative.dev/docs/flatlist
The usual thing is the renderItem callback renders ONE item at a time, like this:
<FlatList
data={messages}
renderItem={({item}) => {
return <Message message={item} />;
}}
/>
e.g. I'd make a <Message/> component that renders one item only.

How to pass onPress to props.children?

I'm trying to make a wrapper component in react-native that I can pass down all its props to the children it wraps around. What I really want is to pass down all function props down to all its children. It looks something like this below. I want the onPress in Wrapper to be called when the TouchableOpacity is pressed.
I tried this below but it doesn't work
const Wrapper = ({children,...props})=>{
return <View {...props}>{children}</View>
}
const App = ()=>{
return (
<View style={{flex:1}}>
<Wrapper onPress={()=>{console.log(2)}}>
<TouchableOpacity/>
</Wrapper>
</View>
)
}
It looks like you're looking to map the children and apply the props to each one. That might look like this:
const Wrapper = ({children,...props})=>{
return (<>
{React.Children.map(children, child => (
React.cloneElement(child, {...props})
))}
</>);
}
(method of mapping the children borrowed from this answer)
const App = () => {
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
// do the action need here here
}}>
<Wrapper >
</Wrapper>
</TouchableOpacity>
</View>
)
}
I would advise you to use hooks function instead
If you try to reuse functions that are related
** useAdd.js **
export default () => {
const addFuction(a, b) => {
// do preprocessing here
return a + b
}
return [addFuction]
}
main componet
import useAdd from "/useAdd";
const App = () => {
const [addFuction] = useAdd()
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
addFuction(4,5)
}}>
...action component...
</TouchableOpacity>
</View>
)
}
console in useAdd hook.... to see visual changes use the react useState

Maximum update depth exceeded new state React-Native

const [number,setNum] = useState(0); I get this error when I want to add and change it(setNum(number+1)). My Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. What can i to solve this?
const App = ()=>{
const [text,setText] = useState('');
const [todo,setToDo] = useState([]);
const [number,setNum] = useState(0);
const renderToDoCard = ({item})=>{
setNum(number+1)
return(
<TouchableHighlight
onLongPress={() => handleLongPress(item)}>
<ToDoCard todo={item} number={number}/>
</TouchableHighlight>
)
}
const handleLongPress = item => {
setToDo(todo.filter(i => i !== item));
return Alert.alert('Silindi');
};
return(
<SafeAreaView style={styles.container}>
<StatusBar backgroundColor='#102027'/>
<View style={styles.head_container}>
<Text style={styles.title}>Yapılacaklar</Text>
<Text style={styles.title}>{todo.length}</Text>
</View>
<View style={styles.body_container}>
<FlatList data={todo} renderItem={renderToDoCard} />
</View>
<View style={styles.bottom_container}>
<ToDoInput todo={todo} setToDo={setToDo} text={text} setText={setText}/>
</View>
</SafeAreaView>
)
}
You've created an infinite update loop.
The problem is in how you're updating your number state inside renderToDoCard
const renderToDoCard = ({item}) => {
setNum(number + 1); // This is the problem, remove this line
return (
<TouchableHighlight onLongPress={() => handleLongPress(item)}>
<ToDoCard todo={item} number={number} />
</TouchableHighlight>
);
};
When renderToDoCard renders you update the state of your App component so it rerenders App which renders renderToDoCard which updates the state of your App component so it rerenders App which renders renderToDoCard...
This process repeats until the max update depth is reached.
Simply remove setNum(number + 1); and that problem is fixed.
It seems to me from your code that all you use your number state for is to keep track of the current item index so you can pass this to the ToDoCard component. The FlatList's renderItem also provides access to the current item index which you could pass to the number prop of ToDoCard
renderItem({ item, index, separators });
https://reactnative.dev/docs/flatlist#required-renderitem
So you could instead do something like this
const renderToDoCard = ({item, index}) => {
return (
<TouchableHighlight onLongPress={() => handleLongPress(item)}>
<ToDoCard todo={item} number={index} />
</TouchableHighlight>
);
};
Alternative you can add a key to each item in todo and use that instead of the index.

Invalid Hook Call - React Hooks

I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}

Render method called twice with componentDidMount - React native

I'm fetching some data from the firebase realtime database. I have created a state in the constructor and initialised as an empty array. Later in the componentDidUpdate method, I have updated the state with setState method. The issue is the render method called twice in the component and data is getting multiplied each time.
this.state = {
values: [],
}
componentDidMount = () => {
firebase.database().ref('Table').once('value', (data) => {
var input = data.val();
this.setState({ values: input })
})
}
And the render method:
var val = []; //global variable declared before class declaration
render() {
{
this.state.values.map(item => {
val.push(
<List>
<ListItem>
<Text>{item["value"]}</Text>
</ListItem>
</List>
)
})
}
return(
<View>
{val}
</View>
)
}
And the list item is keep getting multiplied each time when the component renders. I have checked the doc but couldn't get a proper solution.
https://reactjs.org/docs/react-component.html#componentdidmount
Where is val defined?
Okay. That I have defined a global var. Declared it as an array before the class declaration
That's where your duplication comes from.
Better do it this way:
render() {
const val = this.state.values.map((item, index) => (
<List key={index}>
<ListItem>
<Text>{item.value}</Text>
</ListItem>
</List>
));
return <View>{val}</View>;
}
I didn't understand well the val variable but this code should work for you:
mapValues = list => list.map((item, index) => (
<List key={index}>
<ListItem>
<Text>{item.value}</Text>
</ListItem>
</List>
));
render() {
return (
<View>
{this.mapValues(this.state.values)}
</View>
);
}

Categories