Is there a way to replace a component onPress in React Native? - javascript

In React-Native I am using this component <SimpleLineIcons name='heart' size={32} style={{position: 'absolute', left: 10}}/> I want so that when I click on it, it gets replaced with this component <AntDesign name='heart' size={32} color="red" style={{position: 'absolute', left: 10}}/>. How can I do this?

Create an item in the state, called toggleIcon. This will dictate what component to display. Then, to conditionally render the two component simply use a ternary operator like this:
export const MockComponent = () => {
const [toggleIcon, setToggleIcon] = React.useState(false);
return (
<>
// ...
{toggleIcon ? <SimpleLineIcon onClick={() => setToggleIcon(!toggleIcon)} /> : <AntDesign <SimpleLineIcon onClick={() => setToggleIcon(!toggleIcon)} />}
</>
)
}

Related

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

ternary operator inside a component tag

Is it possible to use ternary operator within a built-in component tag? For instance, I am using Touchable Opacity from React Native (Native Base):
type ItemProps = {
title: string;
face: string;
};
export const Item: React.FunctionComponent<ItemProps> = ({
title,
face,
}) => {
const [showAddFriendPage, setShowAddFriendPage] = useState(false);
const toggleAddFriendPage = () => {
setShowAddFriendPage(showAddFriendPage ? false : true);
};
return (
<TouchableOpacity activeOpacity={0.8}
onPress={() =>
setShowAddFriendPage(true)
} >
<View>
<Thumbnail small source={{ uri: face }} style={styles.thumbnail} />
<Text numberOfLines={1} style={styles.title}>
{title}
</Text>
<AddFriendPage
showAddFriendPage={showAddFriendPage}
toggleShowPage={toggleAddFriendPage}
/>
</View>
</TouchableOpacity>
);
};
Currently the onPress navigation is applied to all Items regardless of what title or face was used. I want to introduce a conditional navigation. For instance, if the
title == 'news'
then onPress.... Since we can't use if else statements within jsx, I was trying ternary operators:
<TouchableOpacity activeOpacity={0.8}
{title == 'news'? {
onPress={() =>
setShowAddFriendPage(true)
}
} }
/>
But this clearly doesn't work. I get '...' expected.on title.
No value exists in scope for the shorthand property 'onPress'. Either declare one or provide an initializer.ts(18004)on onPressand
Cannot find name 'setShowAddFriendPage'.
you can do like this
<TouchableOpacity activeOpacity={0.8}
onPress={() =>{
if(title == 'news'){
setShowAddFriendPage(true)
}
}}
/>
You can use spread operator (...) to conditionally add props to components.
<TouchableOpacity
activeOpacity={0.8}
{...(title == 'news' && { onPress: () => setShowAddFriendPage(true) })}
/>
This way component will have onPress prop whenever title equals to 'news'
Use useCallback to create an onPress function that has different behavior based on your condition.
const onPress = useCallback(() => {
if (title === 'news') {
setShowAddFriendPage(true)
}
}, [title])
It has a dependency on title, so it will be re-created, and the component re-rendered only if title changes.
Then use it as such:
<TouchableOpacity activeOpacity={0.8} onPress={onPress}>
{/* … */}
</TouchableOpacity>

How to hide sidebar in React using Ant Design

I'm trying to do a sidebar that hides on the clicking of an icon in the navigation bar. And I don't want to use classes, maybe I'm wrong doing it this way, but I want to keep it as much as possible. I've got an error that says:
(property) collapsed: boolean ';' expected.ts(1005)
In the const toggle:
const state = {
collapsed: true
};
const toggle = () => {
state.collapsed: !state.collapsed
};
const Sidebar = () => {
return (
<Layout.Sider collapsed={state.collapsed} style={{ backgroundColor: '#f0f0f0' }}>
...
</Layout.Sider>
)
}
In the navigation bar I got this:
<Button
type="primary"
shape="circle"
icon="user"
size={'small'}
style={{ marginLeft: '10px' }}
onClick={() => toggle}
/>
My layout:
const Layout = ({ children }: LayoutProps) => {
return (
<AntdLayout>
<AntdLayout>
<Header />
</AntdLayout>
<div>{children}</div>
<Sidebar />
</AntdLayout>
)
}
Thank you all!
There are two things what I found as an issue in your code. If you have functional component, you can use useState for updating boolean state. The other one is how you use onClick={() => toggle}, technically you are not calling the function, just returning.
I think you can try the following - creating a boolean variable with state hook:
const Sidebar = () => {
const [collapsed, setCollapsed] = useState(true);
return (
<Layout.Sider collapsed={state.collapsed} style={{ backgroundColor: '#f0f0f0' }}>
...
</Layout.Sider>
)
}
And in the button, you can use as the following - toggling the value of collapsed variable:
<Button type="primary"
shape="circle"
icon="user"
size={'small'}
style={{ marginLeft: '10px' }}
onClick={() => setCollapsed(!collapsed)} />
Read further here:
State hook
Function and Class components
If you are interested in further, I have prepared earlier a repository on GitHub to present toggling elements in class, functional or styled components. Find it here: norbitrial/react-toogle-class-on-click
I hope this helps!

How would I dynamically append duplicate components in react, react-native

I am confused about how to properly dynamically add/create same components on button press for react native. I have used .map(()=>{}) on existing info to create components and then display the results.
Would I have to save each new component into a setstate array, then map that?
I looked a little into refs, but wasn't sure how that was better than just a setstate. The problem I see is if I want to update the value for each component, how would I go about that if their all originally duplicates?
Something along the lines of this:
class SingleExercise extends Component {
constructor(props) {
super(props);
this.state = {
objInfo: this.props.navigation.getParam("obj"),
currentSetSelected: 0
};
this.addSet = this.addSet.bind(this);
}
addSet = () => {
return (
<addSet />
)
}
render() {
const { navigation } = this.props;
return (
<View style={{ flex: 1 }}>
<View style={{ height: 80 }}>
<addSet />
<View>
<Button //here
large={false}
onPress={() => {
this.addSet();
}}
title={"add more"}
/>
</View>
</View>
</View>
);
}
}
const addSet = () => {
return (
<TouchableHighlight>
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
defaultValue={'test'}
onChangeText={(text) => this.setState({text})}
/>
</View>
</TouchableHighlight>
);
}
Here is what I would do:
Every click on addSet button should increment the AddSets counter like this:
<Button
large={false}
onPress={() => {
this.setState({addSetsCounter: this.state.addSetsCounter});
}}
title={"add more"}
/>
After every state update your component will be re-rendered. So now, all you need to do is to forLoop in through that counter and return as many AddSets components as needed. A simple for loop with .push() inside would do.
Inside render, before return place something like that:
let sets =[];
for(let i =0;i<this.state.addSetsCounter;i++){
sets.push(<AddSets key="AddSets-{i}"/>);
}
Then simply render your {sets}.
I cannot fully test that right now, I wrote that from the top of my head, just play with the above, at least I hope it points you in a right direction.

React (native) navigation toggle search bar

Have been struggling a couple of days now trying to figure out how to toggle a search bar in the react navigation.
My approach has been to
static navigationOptions = ({navigation}) => {
return {
title: 'Header Title',
headerLeft: (
{navigation.params.state.search ? <searchfield query={text => navigation.setParams(text)} > : <settings>}
),
headerRight: (
<TouchableOpacity style={{ marginHorizontal: 10 }}>
<Icon name="search" size={28} color="#5751D9" />
</TouchableOpacity>
)
}}
I then wanted to add some logic to the headerLeft so it either returns the cog icon button component or an TextInput component (plan to pass the text to setParams and use it as a filter in the list component below the header) but I can't seem to figure out how to pass down a state or state handler as props when I'm not navigating to it.. It's the initial screen.
Hook a function to your setParams inside componentDidMount which will
be called on searchedText change, use this function to setState.
componentDidMount() {
this.props.navigation.setParams({onSearchText: (searchedText) => this.onSearchText(searchedText)});
}
onSearchText(searchedText) {
//update your list using this searchedText
this.setState({searchedText})
}
Now call the function onSearchText() when searchedText changes,
static navigationOptions = ({navigation}) => {
return {
title: 'Header Title',
headerLeft: (
{navigation.params.state.search ? <searchfield query={text => onSearchText(text)} > : <settings>}
),
headerRight: (
<TouchableOpacity style={{ marginHorizontal: 10 }}>
<Icon name="search" size={28} color="#5751D9" />
</TouchableOpacity>
)
}}
Hope it will help you ...

Categories