How to implement custom component that accepts a nested component? - javascript

I would like to implement a custom component that accepts a nested component. Something that can be used like this.
<MyCustomComponent>
<AnyNestedComponent/>
</MyCustomComponent>
I searched about this but only found the use of this.props, which is not what I expect.
How do I implement MyCustomComponent in React Native version 0.68?
Note: MyCustomComponent will consist of View(s).

Its fairly simple in RN,
Your customComponent should be like =
const CumstomComp = ({props = {}, children = null}) => {
return(
<View style={{backgroundColor:"red"}} >
{children}
</View>
)
}
and then you use it like this
App.js or whatever file
const App = () => {
return(
<View>
<CustomComp>
<Flatlist />
<View />
</CustomComp>
</View>
)
}
Hope it helps. feel free for doubts

Related

Image not rendering in react native (Expo)

I am created a simple box component which takes image source as props. But while calling HomeScreenBox the image is not rendering..
<HomeScreenBox
BoxName="Add Marks"
BoxImage='require("../assets/images/person.jpg")'
/>
const HomeScreenBox = (props) => {
return (
<View>
<Image source={props.BoxImage} />
<Text>{props.BoxName}</Text>
</View>
);
};
require should not be a string, try
BoxImage={require("../assets/images/person.jpg")}

Compose components to variable in reactjs

Is it possible to compose more different parts of component to variable?
documentation
const App = () => {
let element;
element = <View>
<Text>text</Text>
</View> // --> OK
element = element + <View>
<Text>text2</Text>
</View> // --> TypeError: Cannot read properties of undefined (reading 'height')
element.push(<View>
<Text>text3</Text>
</View>); // --> TypeError: element.push is not a function
return <>
{element}
</>
}
export default App;
I use reactjs 17.0.2, typescript and "#react-pdf/renderer": "2.3.0".
Update
Based on your question here, this should work:
<Document>
<Page size="A4" orientation="landscape">
{/* -- Table LEFT: -- */}
<View>
{/* -- Table Head: -- */}
<View>
<Text>Index</Text>
<Text>Brand</Text>
<Text>Type</Text>
</View>
{/* -- Table Body: -- */}
{data?.cars?.length &&
data.cars.map(({ id, brand, type }, index) => (
<View key={`${id}-left`}>
<Text>{index + 1}</Text>
<Text>{brand || ''}</Text>
<Text>{type || ''}</Text>
</View>
))}
</View>
</Page>
<Page size="A4" orientation="landscape">
{/* -- Table RIGHT: -- */}
<View>
{/* -- Table Head: -- */}
<View>
<Text>Color</Text>
<Text>Fuel</Text>
</View>
{/* -- Table Body: -- */}
{data?.cars?.length &&
data.cars.map(({ id, color, fuel }) => (
<View key={`${id}-right`}>
<Text>{color || ''}</Text>
<Text>{fuel || ''}</Text>
</View>
))}
</View>
</Page>
</Document>
The issue seems to be with how you're handling arrays, not with rending React elements.
If you want to access the properties of the object in an array element, you can destructure the element, so instead of
data.cars.map((car, index) => (<Text>{car.color}</Text>))
you can do
data.cars.map(({id, brand, type, color, fuel}, index) => (<Text>{color}</Text>));
If you're not performing any operations on the array elements, you can use an implicit return instead of an explicit return:
// explicit return
data.cars.map(({id, brand, type, color, fuel}, index) => {
// do something else here
return (
<Text>{color}</Text>
)
});
// implicit return
data.cars.map(({id, brand, type, color, fuel}, index) => (<Text>{color}</Text>));
Also, when you're rending known text values in React, you don't need to wrap it in curly braces ({}), you can just render the text directly.
Instead of
<Text>{'color'}</Text>
you can just put
<Text>color</Text>
unless it's required by whatever library you're using. I'm not familiar with #react-pdf/renderer.
One more thing to keep in mind is that the key for list items in React should be something stable. Using array indices as keys is discouraged (see React docs).
Original answer
If you want to render an element this way, you could do something like this:
const App = () => {
let element = [];
// Each child in a list needs a unique "key" prop
element.push(<View key={someUniqueKey}>
<Text>text</Text>
</View>)
element.push(<View key={someOtherUniqueKey}>
<Text>text2</Text>
</View>)
element.push(<View key={oneMoreUniqueKey}>
<Text>text3</Text>
</View>);
return <>
{element}
</>
}
export default App;
Personally, I haven't seen anyone render components like this.
The strategy you are looking for is called conditional rendering, and there are different ways to do this depending on the situation.
For example, if you're trying to dynamically render data from an API response, you could do something like this:
const App = () => {
const { data } = fetchDataFromAPI();
return (
<>
<View>
<Text>text</Text>
</View>
{data?.text2 && (
<View>
<Text>{data.text2}</Text>
</View>
)}
{data?.text3 && (
<View>
<Text>{data.text3}</Text>
</View>
)}
</>
);
};
export default App;
You can check out the React docs for conditional rendering and rendering lists.
(Note: The above links are for the beta docs. If you prefer the classic(?) docs: conditional rendering and lists)

Pass function from one screen to another in react native

Im trying to pass a function handleNewFavourite (which updates my favouriteList state array) from my HomeScreen to my DetailsScreen via navigation params but Im getting the following error: Non-serializable values were found in the navigation state
How should I pass functions that modified the state between different stack screens?
HomeScreen code:
<FlatList
data={checkCategory()}
renderItem={({item}) => (
<TouchableOpacity
onPress={() =>
navigation.navigate('Details', {
item,
handleNewFavourite,
})
}>
<LessonCard lesson={item} />
</TouchableOpacity>
)}
/>
DetailScreen code:
const LessonDetails = ({lesson, handleNewFavourite}: LessonProps) => {
const [favourite, setFavourite] = useState<boolean>(lesson.favourite);
return (
<LessonDetailsContainer>
<LessonDetailsInfoContainer>
<LessonDetailsCategoryHead>
<LessonDetailsCategory>{lesson.category}</LessonDetailsCategory>
<TouchableOpacity
onPress={() => {
setFavourite(!favourite);
handleNewFavourite(lesson);
}}>
<LessonDetailsFavouriteIcon>
{favourite ? '❤️' : '🤍'}
</LessonDetailsFavouriteIcon>
</TouchableOpacity>
</LessonDetailsCategoryHead>
<LessonDetailsTitle>{lesson.title}</LessonDetailsTitle>
<LessonDetailsAuthor>{lesson?.author}</LessonDetailsAuthor>
</LessonDetailsInfoContainer>
<LessonDetailsCardImage
source={{
uri: lesson.image,
}}
/>
<LessonDetailsContentContainer>
<LessonDetailsDescriptionText>
{lesson.content}
</LessonDetailsDescriptionText>
</LessonDetailsContentContainer>
</LessonDetailsContainer>
);
};
export default LessonDetails;
For situation like this, you should learn global state management. ( Context API - Redux etc. )
I think you are disrupting in the wrong way the parameters passed to DetailScreen it should be something like this:
const LessonDetails = ({route}: LessonProps) => {
const {lesson, handleNewFavourite} = route.params;
// The rest of your component here
}
As the documentation here suggested. But as #omerfarukose mentioned is not a bad idea to use state management in this particular scenario

React Native Styled Components how to pass non specific styles as props for re usable components

Given the following simple component example which uses styled components for the Pressable:
const StyledPressable = styled.Pressable = props => props.buttonStyle
const Icon: FC<{buttonStyle: string}> = ({buttonStyle}) => {
return (
<StyledPressable buttonStyle={buttonStyle} >
<SomeIconComponent/>
</StyledPressable>
)
}
I would like to create a reusable component which takes in style props while still using styled components. Normally using the vanilla react native styles, this is really simple one would just pass in the style object as a prop an use it as a value in the necessary component. How would i achieve this behavior using styled components? my initial guess is that the buttonStyle has to be a string in the same format as styled components, but how would i make it so that the style which is declared inside the '' of lets say StyledPressable in this example is equal to the passed buttonStyle prop?
Since i am learning styled components, i am very open to any other way to achieve this behavior while using styled components.
import styled from "styled-components/native"
const Pressable = styled.Pressable(props => (props.buttonStyle))
export default function App() {
return (
<View style={styles.container}>
<Pressable buttonStyle={{ backgroundColor: "pink" }}>
<Text>This is button</Text>
</Pressable>
</View>
);
}
Inder's answer using some Typescript
import styled from "styled-components/native"
interface PressableProps {
buttonStyle?: CSSObject
}
const Pressable = styled.Pressable<PressableProps>({ buttonStyle }) => buttonStyle)
export default function App() {
return (
<View style={styles.container}>
<Pressable buttonStyle={{ backgroundColor: "pink" }}>
<Text>This is button</Text>
</Pressable>
</View>
);
}

Cannot set property '0' of null in React Native ref

I am trying to set up a React Native ref like here, only in a class component:
https://snack.expo.io/PrrDmZ4pk
Here's my code:
class DetailBody extends Component {
constructor() {
super();
this.myRefs = React.createRef([]);
}
clickText(index) {
this.myRefs.current[index].setNativeProps({ style: { backgroundColor: '#FF0000' } });
}
render() {
if (this.props.article.json.results.length === 0) {
return <Loading />;
}
return (
<View >
<View>
<View ref={this.props.highlight} nativeID="some-id" >
{this.props.article.json.results.map((content, index) => (
<TouchableOpacity onPress={() => this.clickText(index)}>
<View key={index} ref={el => this.myRefs.current[index] = el}>{content}</View>
</TouchableOpacity>
</View>
</View>
</View>
This should theoretically let me add a background colors when my ref is clicked, much like the snack I linked to above.
However what I actually see is this:
This seems to be related to .current inside my ref being null, despite passing a default value.
How do I fix this error?
If the ref callback is defined as an inline function, it will get called twice during updates, first with null and then again with the DOM element.
Haven't really used it this way but I think you might just need to do this in the constructor:
this.myRefs = React.createRef();
this.myRefs.current = [];

Categories

Resources