Is there a way to "inject" one RN component into another in a specific place.
Say I have this component:
const Original = () => {
return (
<View>
<Text>Hello</Text>
{InsertChildComponentHere}
</View>
)
}
const ChildComponent = () => {
return (
<View>
<Text>I am a child component</Text>
</View>
)
}
To inject a Component into a other Component (HOC) your component has to accept "Component" as params. You can write it like this:
const Original = (Component) => {
const newComponent = ({ ...props }) => {
return (
<Fragment>
<Text>Hello</Text>
<Component {...props} />
</Fragment>
);
};
return newComponent;
};
to create the HOC you can write:
const MyComponent = withOriginal(ChildComponent);
Related
I am using a the same component (BottomSheet) on two different screens in my react navigation stack.
When I go from one page to another, it keeps the component state. How can I refresh the component when I browse to another screen?
const HomeScreen = () => {
...
const showSheet = data.reminderVehicles.length !== 0 ? true : false;
return (
<BottomSheet
firstSnapshot={160}
secondSnapshot={90}
renderContent={renderSheetContent()}
tapToOpenEnabled
headerClose={true}
hide={showSheet}
>
.....
</BottomSheet>
);
};
export default HomeScreen;
export const MyVehicleScreen = ({ navigation }) => {
return (
...
<BottomSheet
firstSnapshot={160}
secondSnapshot={90}
renderContent={renderSheetContent()}
tapToOpenEnabled
hide={false}
>
</BottomSheet>
</RACPageStructure>
);
};
BottomSheet component:
const BottomSheet = ({
children,
firstSnapshot,
secondSnapshot,
renderHeader,
renderContent,
backgroundColor,
tapToOpenEnabled,
headerClose,
hide,
}) => {
...
return (
<WrapperComponent onPress={toggleOpen}>
<Animated.View style={childrenStyle}>{children}</Animated.View>
{!hide && (
<PanGestureHandler onGestureEvent={handleSwipe}>
....
</Animated.View>
</PanGestureHandler>
)}
</WrapperComponent>
);
};
Navigation stack:
return (
<TabStack.Navigator
initialRouteName={initialRouteName}
tabBarOptions={{
...
}}
>
<TabStack.Screen
name={AppRoutes.Home.Name}
...
</TabStack.Screen>
<TabStack.Screen
name={AppRoutes.MyVehicle.Name}
...
</TabStack.Screen>
So, I want to consume a context TaskContext in just two components. There is a way to do this, without wrapping my whole application with the TaskContext ? Furthemore, what would be the best way to handle multiples context ?
I've try wrapping a single component with the context by creating another function that returns a new component. For example:
const HomePageContext = (props) => {
return (
<TaskProvider>
<HomePage {...props}/>
</TaskProvider>
)
}
But with this approach, the data update in AddTask screen doesn't appear in HomePage.
(Disclaimer: I just want to create a homePage that contains a list of tasks. This tasks are created in another screen, AddTasks. When the task is created, the homepage should be update to display the new list, with all other tasks and the new one. Thus, i dont know what is the best approach to do this.)
My code is here. It's only for test and practice purpose.
import React, { createContext, useReducer, useContext} from 'react'
import { View, FlatList, Text, Button } from 'react-native'
import { createNativeStackNavigator } from '#react-navigation/native-stack'
import { createDrawerNavigator } from '#react-navigation/drawer'
import { NavigationContainer } from '#react-navigation/native'
const TaskContext = createContext({})
const initialState = [{ name: `Task name ${Math.floor(Math.random() * 100)}`, id: Math.floor(Math.random() * 100) }]
//contexto provider
const TaskProvider = ({ children }) => {
const reducer = (state, action) => {
switch(action.type) {
case 'addTask':
console.log([...state, action.payload])
return [...state, action.payload]
default:
return state
}
}
const [tasks, dispatch] = useReducer(reducer, initialState)
return (
<TaskContext.Provider value={ { tasks, dispatch } }>
{ children }
</TaskContext.Provider>
)
}
const HomePage = ({ navigation }) => {
const { tasks, dispatch } = useContext(TaskContext)
return (
<View>
<Text>Home</Text>
<FlatList
data={tasks}
keyExtractor={item => `${item.id}`}
renderItem={({ item }) => <Text>{item.id} - {item.name}</Text>}
/>
<Button
title='Add Task'
onPress={() => navigation.navigate('AddTask')}
/>
</View>
)
}
const Info = () => {
return (
<View>
<Text>Info</Text>
</View>
)
}
const AddTaskPage = () => {
const { dispatch } = useContext(TaskContext)
const newTask = {id: Math.floor(Math.random() * 100), name: `Task name ${Math.floor(Math.random() * 100)}`}
return (
<View>
<Text>addTaskPage</Text>
<Button
title='AddTask'
onPress={() => dispatch({
type: 'addTask',
payload: newTask
})}
/>
</View>
)
}
// createNavigators
const Stack = createNativeStackNavigator()
const Drawer = createDrawerNavigator()
const DrawerNavigation = () =>{
return (
<Drawer.Navigator>
<Drawer.Screen name='Home' component={HomePage}/>
<Drawer.Screen name='Info' component={Info}/>
</Drawer.Navigator>
)
}
// App component
export default App = () => {
return (
<TaskProvider>
<NavigationContainer>
<Stack.Navigator initialRouteName='Menu'>
<Stack.Screen name='Menu' options={{ headerShown: false }} component={DrawerNavigation} />
<Stack.Screen name='AddTask' component={AddTaskPage} />
</Stack.Navigator>
</NavigationContainer>
</TaskProvider>
)
}
I'm facing an issue whenever i tried to navigate to another screens. I'm using the navigation in child component and it doesn't work even i passed the props to the parent component. This is my first time on using react navigation. I've tried many possible solution yet still can't solve this issue. I'm using react navigation 5 and i need help. I'm getting an error as such :
TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate')
Home.js // Parent Component
class Home extends Component {
render() {
return (
<Cards
title="In Progress"
imgUri={require('../assets/CardProgress.png')}
navigateAction={() =>
this.props.navigation.navigate('SiteAudit')
}
)
}
}
Card.js // Child Component
const Cards = (props) => {
return (
<CardContainer
backgroundColor={props.backgroundColor}
onPress={() => {
props.navigation.navigateAction;
}}>
<CardWrapper>
<CardTitle>{props.title}</CardTitle>
<CardImage source={props.imgUri} />
</CardWrapper>
</CardContainer>
);
};
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import Dashboard from './screens/Dashboard';
import SiteAudit from './screens/SiteAudit';
const RootStack = createStackNavigator();
const DashboardStack = createStackNavigator();
const SiteAuditStack = createStackNavigator();
const DashboardScreen = () => {
return (
<DashboardStack.Navigator>
<DashboardStack.Screen name="Dashboard" component={Dashboard} />
</DashboardStack.Navigator>
);
};
const SiteAuditScreen = () => {
return (
<SiteAuditStack.Navigator>
<SiteAuditStack.Screen name="SiteAudit" component={SiteAudit} />
</SiteAuditStack.Navigator>
);
};
const Navigation = () => {
return (
<NavigationContainer>
<RootStack.Navigator initialRouteName="Dashboard">
<RootStack.Screen name="Dashboard" component={DashboardScreen} />
<RootStack.Screen name="SiteAudit" component={SiteAuditScreen} />
</RootStack.Navigator>
</NavigationContainer>
);
};
export default Navigation;
Edit your card view as follows
<Cards
title="In Progress"
imgUri={require('../assets/CardProgress.png')}
onPress={() => this.buttonPress()}
/>
const buttonPress = () => {
this.props.navigation.navigate('Site Audit');
};
Edit your button as follows,
<TouchableOpacity
style={styles.cardButton}
onPress={onPress}
<Text style={styles.cardTitle}>{this.props.title}</Text>
<Image style={styles.imageCard} source={this.props.imgUri} />
</TouchableOpacity>
Solved the issue, I just need to re-read the documentation of react navigation. I need to use the useNavigation that has been stated here https://reactnavigation.org/docs/use-navigation/
Cards.js // Child Component
const Cards = (props) => {
return (
<CardContainer
backgroundColor={props.backgroundColor}
onPress={props.navigationAction}>
<CardWrapper>
<CardTitle>{props.title}</CardTitle>
<CardImage source={props.imgUri} />
</CardWrapper>
</CardContainer>
);
};
Home.js // Parent Component
import {useNavigation} from '#react-navigation/native';
const Home = () => {
const navigation = useNavigation();
return (
<Cards
title="Completed"
backgroundColor="#0082C8"
navigationAction={() => {
navigation.navigate('Site Audit');
}}
/>
)
}
I have this code, using React.useRef() but not working:
Main.js:
import * as React from "react"
export const Main: React.FunctionComponent<Props> = observer((props) => {
const ref = React.useRef()
React.useEffect(() => {
///Can not get ref from message
ref.gotoPosition(5)
}, [])
return (
<View style={styles.container}>
<Message
ref={ref}
getGotoIndex={getFunction}
onEndList={isShowQuickMove}
isSpeaker={state.isSpeaker}
questionsList={state.questionsList}
clickQuestion={clickQuestion}
isTyping={chatStore.loading}
data={state.data}/>
</View>
)
}
Message.js:
import * as React from "react"
// eslint-disable-next-line react/display-name
export const Message = React.forwardRef((props, ref) => ({
const { ... } = props
const gotoPosition = (index) => {
console.log('in here')
}
return (
<View>
....
</View>
)
}
)
I can not get ref from Message, even i used React.forwardRef. How to access gotoPosition function in Message by ref like ref.gotoPosition(5). Thanks
You are not passing the ref you get to the Flatlist all you need to do is pass it like so:
<FlatList
ref={ref} // create a referenece like so
extraData={[data, isSpeaker]}
onEndReached={handleEnd}
onEndReachedThreshold={0.4}
data={data}
keyExtractor={(item, index) => index.toString()}
renderItem={renderItems}
/>
I want to render local state in LiestView.
I'm not familiar with ListView,¥.
it didn't work.
export default class Top extends Component {
state=[{title: 'a'}, {title: 'b'}, {title: 'c'}]
_renderItem = ({item}) => (
<View>
<Text>{item.key}</Text> // here
<Category />
</View>
);
render() {
return (
<FlatList
data={this.state.data}
renderItem={this._renderItem(item)}
/>
);
}
}
below is my code. it workes. How to change this code?
I need title.
export default class Top extends Component {
state={
data:[{}, {}, {}]
};
_renderItem = () => (
<View>
<Category />
</View>
);
render() {
return (
<FlatList
data={this.state.data}
renderItem={this._renderItem}
/>
);
}
}
thanks for your time.
The state must be an object and if you don't have a key prop, then you need to define a keyExtractor:
import React, { Component } from "react";
import { Text, View, FlatList } from "react-native";
export default class Top extends Component {
state = {
data: [{ id: 1, title: "a" }, { id: 2, title: "b" }, { id: 3, title: "c" }]
};
_renderItem = ({ item }) => (
<View>
<Text>{item.title}</Text>
</View>
);
// You could use the title instead of the id, but not very scalable
_keyExtractor = (item, index) => item.id;
render() {
return (
<FlatList
data={this.state.data}
renderItem={this._renderItem}
keyExtractor={this._keyExtractor}
/>
);
}
}
First Import ListView
import {View, Text, ListView} from 'react-native';
in your component will mount create data source like this
componentWillMount(){
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.dataSource = ds.cloneWithRows(this.props.yourProps);
}
render the list view
render() {
return (
<View>
<ListView
enableEmptySections
dataSource = {this.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
finally you have to let the ListView know which data and how you want to show the data in the list view. For this create a function that renders the rows of the list View
renderRow(yourProps) {
return (
<Text>{yourProps.yourData}</Text>
);
}
You can style the row whatever way you want.
For more you can check this repo in gitHub: https://github.com/ishraqe/manage-user/blob/master/src/components/EmployeeList.js