Currently I am exporting a component
export default function ChooseNamespace() {
return (
<View>
<Text>HELLO</Text>
</View>
);
}
And importing it here:
import ChooseNamespace from "./screens/ChooseNamespace";
const AppDrawerNavigator = () => {
console.log(storeState.userNamespace);
if (storeState.userNamespace === null) {
return <ChooseNamespace />;
} else {
return (
<AppDrawer.Navigator
drawerContent={(props) => <AppDrawerContent {...props} />}
drawerContentOptions={{
activeTintColor: "blue",
labelStyle: {
color: "white",
},
}}
>
<AppDrawer.Screen
name="Chat"
children={(props) => <ChatScreen name="Blindly Chat" {...props} />}
/>
<AppDrawer.Screen
name="Profile"
children={(props) => <ProfileScreen name="Profile" {...props} />}
/>
<AppDrawer.Screen
name="DirectMessages"
children={(props) => <DMScreen {...props} />}
/>
<AppDrawer.Screen
name="Threads"
children={(props) => <ThreadsScreen {...props} />}
/>
<AppDrawer.Screen
name="Settings"
children={(props) => <SettingsScreen {...props} />}
/>
<AppDrawer.Screen
name="Support"
children={(props) => <SupportScreen {...props} />}
/>
</AppDrawer.Navigator>
);
}
};
Which is then throwing the error
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of ChooseNamespace.
Can someone try an explain the issue here? I am having a bit of trouble finding it.
const ChooseNamespace =()=> {
return (
<View>
<Text>HELLO</Text>
</View>
);
}
export default ChooseNamespace
Try this code. ES6 doesn't allow export default const. You must declare the constant first then export it. Or if WANT to have a function then do this:
function ChooseNamespace() {
return (
<View>
<Text>HELLO</Text>
</View>
);
}
export default ChooseNamespace;
Can you try this.
return (
<>
{
storeState.userNamespace === null?
<ChooseNamespace/>:
<YourDrawerView/>
}
</>
);
Related
New to React Native I recently followed the navigation tutorial https://www.robinwieruch.de/react-native-navigation/. I then decided to put the sign out function into the draw navigator which opened up a whole can of worms around passing props. I’ve read so much stuff on that but still can’t figure out what’s going on here, and hope someone can explain it to me.
Without copying the whole code (and I hope I've copied enough), inside App() I have the following:
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
const handleSignIn = () => {
// TODO implement real sign in mechanism
setIsAuthenticated(true);
};
const handleSignOut = () => {
// TODO implement real sign out mechanism
setIsAuthenticated(false);
};
…
<Stack.Screen
name="Home"
component={() => <HomeDrawer someText="Mick" handleSignOut={handleSignOut}/>}
…
Outside of App() I have the following:
const HomeDrawer = (props) => {
console.log("In HomeDrawer props = " + JSON.stringify(props));
return (
<Drawer.Navigator
screenOptions={{ headerShown: false }}
initialRouteName="Home Page"
drawerContent={(props) => <CustomDrawerSignOut {...props}/>}
>
<Drawer.Screen name="Home Page" component={HomePage} />
<Drawer.Screen name="Account Details" component={AccountScreen} />
</Drawer.Navigator>
);
};
const CustomDrawerSignOut = (props) => {
console.log("In CustomDrawerSignOut props = " + JSON.stringify(props));
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem label="Sign Out" onPress={props.handleSignOut} />
</DrawerContentScrollView>
);
}
The log output then gives me the following:
In HomeDrawer props = {"someText":"Mick"}
In CustomDrawerSignOut props = {"state":{"stale":false,"type":"drawer","key":"drawer-hyj-tged8DkJ7nDxNVVWB","index":0,"routeNames":["Home Page","Account Details"],"history":[{"type":"route","key":"Home Page-rf91ybSgClEeaz_f7Qf7a"}],"routes":[{"name":"Home Page","key":"Home Page-rf91ybSgClEeaz_f7Qf7a"},{"name":"Account Details","key":"Account Details-hwXHxAxa5wR-lg9xq7cp3"}],"default":"closed"},"navigation":{},"descriptors":{"Home Page-rf91ybSgClEeaz_f7Qf7a":{"route":{"name":"Home Page","key":"Home Page-rf91ybSgClEeaz_f7Qf7a"},"navigation":{},"options":{"headerShown":false}},"Account Details-hwXHxAxa5wR-lg9xq7cp3":{"route":{"name":"Account Details","key":"Account Details-hwXHxAxa5wR-lg9xq7cp3"},"navigation":{},"options":{"headerShown":false}}}}
'handleSignOut' doesn’t seem to get passed to HomeDrawer and 'someText' doesn’t seem to get passed to CustomDrawerSignOut which does receive a lot of other props. Why are 'someText' and 'handleSignOut' not being passed properly, and how do I fix it?
Thanks
I looks like CustomDrawerSignOut is not being passed the props from the HomeDrawer
Please check the components passed to
<Stack.Screen
name="Home"
component={(props) => <HomeDrawer {...props} someText="Mick" handleSignOut={handleSignOut} />}
....
const HomeDrawer = (props) => {
console.log("In HomeDrawer props = " + JSON.stringify(props));
return (
<Drawer.Navigator
screenOptions={{ headerShown: false }}
initialRouteName="Home Page"
// here
drawerContent={(drawerProps) => <CustomDrawerSignOut {...drawerProps} handleSignOut={props.handleSignOut}/>}
>
<Drawer.Screen name="Home Page" component={HomePage} />
<Drawer.Screen name="Account Details" component={AccountScreen} />
</Drawer.Navigator>
);
};
Hope it solves the problem
I render a right bar button in the router file, when it is tapped I want to call a function from another class that saves the data. It can't be a static method because it needs to access "this" property. If I declare a new instance of the class it won't have a the correct data. So what am I supposed to do?
Navigation screen:
import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';
import { Router, Scene, Actions } from 'react-native-router-flux';
import NewUserScreen from './NewUserScreen';
import AddMealScreen from './AddMealScreen';
export default function App() {
return (
<Router>
<Scene key="root">
<Scene key="newUser"
component={NewUserScreen}
title="App"
initial
/>
<Scene
key="addMeal"
component={AddMealScreen}
title="Add Meal"
renderRightButton={() =>(
<View>
<TouchableOpacity onPress={() => AddMealScreen.saveMeal()}>
<Text>Add</Text>
</TouchableOpacity>
</View>
)}
/>
</Scene>
</Router>
);
}
Add Meal Screen:
export default class AddMealScreen extends Component {
constructor() {
super();
this.state={
mealText: '',
}
}
render() {
return (
<View style={styles.container}>
<TextInput style = {styles.mealTextField}
placeholder = "Meal"
onChangeText={(mealText) => this.setState({mealText})}
/>
</View>
);
}
saveMeal = async () => {
await data.saveString('meal', this.state.mealText)
await data.getStringData('meal');
}
You can call child component function by using ref
you are using functional component therefore, you have to use useRef hook
Code:
Navigation screen:
import React, { Component, useRef } from "react";
export default function App() {
const _AddMealScreen = useRef();
return (
<Router>
<Scene key="root">
<Scene key="newUser" component={NewUserScreen} title="App" initial />
<Scene
initial
key="addMeal"
component={() => <AddMealScreen ref={_AddMealScreen} />}
title="Add Meal"
renderRightButton={() => (
<View>
<TouchableOpacity
onPress={() => _AddMealScreen.current.saveMeal()}
>
<Text>Add</Text>
</TouchableOpacity>
</View>
)}
/>
</Scene>
</Router>
);
}
As you stated since it uses this it cant be a static method, but not only, since it use this you don't need just an instance but you strictly need the rendered instance. You can achieve it through ref.
export default class App extends React.Component {
render() {
return (
<Router>
<Scene key="root">
<Scene key="newUser" component={NewUserScreen} title="App" initial />
<Scene
key="addMeal"
title="Add Meal"
renderRightButton={() =>(
<View>
<TouchableOpacity onPress={() => this.mealScreen.saveMeal()}>
<Text>Add</Text>
</TouchableOpacity>
</View>
)}
>
<AddMealScreen ref={ref => this.mealScreen = ref} />
</Scene>
</Router>
);
}
}
Hope this helps.
Whenever I activate the onPress method by tapping on a message, the MessageScreen component just re-renders rather than displaying ChatScreen. This happens even if I replace ChatScreen with any other screen. Any help on the matter is much appreciated.
App.js
<NavigationContainer ref={containerRef} initialState={initialNavigationState}>
<Drawer.Navigator>
<Drawer.Screen name="Feed" component={BottomTabNavigator} options={{swipeEnabled: false}} />
<Drawer.Screen name="Settings" component={SettingsStack} options={{swipeEnabled: false}} />
</Drawer.Navigator>
</NavigationContainer>
BottomTabNavigator
const BottomTab = createBottomTabNavigator();
export default function BottomTabNavigator({ navigation, route }) {
{...HomeStack Code}
{...ProfileStack Code}
const MyMessagesStack = createStackNavigator();
function MessagesStack() {
return (
<MyMessagesStack.Navigator initialRouteName={"Messages"}
screenOptions={{headerShown: false}}>
<MyMessagesStack.Screen name="Messages" component={MessagesScreen} />
<MyMessagesStack.Screen name="Chats" component={ChatScreen} />
</MyMessagesStack.Navigator>
);
}
return (
<BottomTab.Navigator initialRouteName={INITIAL_ROUTE_NAME} >
<BottomTab.Screen
name="Home"
component={HomeStack}
options={{title: 'Feed'}}
/>
<BottomTab.Screen
name="Messages"
component={MessagesStack}
options={{title: 'Messages'}}
/>
<BottomTab.Screen
name="Profile"
component={ProfileStack}
options={{title: 'Profile'}}
/>
</BottomTab.Navigator>
);
}
MessageScreen.js
//flatscreen to render message components
</View>
<FlatList
data={Data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => props.navigation.navigate('Chats')} >
<Message
image={item.image}
name={item.name}
lastMessage={item.message}
timeStamp={item.timestamp}
opened
/>
</TouchableOpacity>
)}
/>
The reason your components are remounting is because there are components defined inside other components:
function BottomTabNavigator() {
// Here
function MessagesStack() {
// ...
}
// ...
}
You need to define them outside to avoid that:
function MessagesStack() {
// ...
}
function BottomTabNavigator() {
// ...
}
I try to implement an admin ui based on react-admin. Now I have multiple resources, which have the same filter criterias. What is the best way to sync the filters across all resources?
Tried to set filterDefaultValues, filters. Although tried to assign the same Filter-Component to multiple resources.
I have really no idea how to sync "YourFilter" with "MyFilter". Expected: You are on MyData, set a filter based on /apples. You switch to YourData, the filter should be pre-selected.
How to realize it?
// in src/mydata.js
const MyFilter = (props) => (
<Filter {...props}>
<ReferenceInput label="Apple" source="appleid" reference="apples" allowEmpty alwaysOn>
<SelectInput optionText="applename" />
</ReferenceInput>
</Filter>
);
export const MyDataList = (props) => (
<List {...props} title="My Data" filters={<MyFilter />}>
<Datagrid>
// ...
</Datagrid>
</List>
);
// in src/yourdata.js
const YourFilter = (props) => (
<Filter {...props}>
<ReferenceInput label="Apple" source="appleid" reference="apples" allowEmpty alwaysOn>
<SelectInput optionText="applename" />
</ReferenceInput>
</Filter>
);
export const YourDataList = (props) => (
<List {...props} title="Your Data" filters={<YourFilter />}>
<Datagrid>
// ...
</Datagrid>
</List>
);
// in src/App.js
const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="MyData" list={MyDataList} />
<Resource name="YourData" list={YourDataList} />
</Admin>
);
If MyFilter and YourFilter are strictly the same, you can use the same filter component for both.
// filters.js
export const CustomFilter = (props) => (
<Filter {...props}>
<ReferenceInput label="Apple" source="appleid" reference="apples" allowEmpty alwaysOn>
<SelectInput optionText="applename" />
</ReferenceInput>
</Filter>
);
// ResourceA.js
import { CustomFilter } from './filters';
export const ResourceAList = (props) => (
<List {...props} title="My Data A" filters={<CustomFilter />}>
<Datagrid>
// ...
</Datagrid>
</List>
);
// ResourceB.js
import { CustomFilter } from './filters';
export const ResourceBList = (props) => (
<List {...props} title="My Data B" filters={<CustomFilter />}>
<Datagrid>
// ...
</Datagrid>
</List>
);
// in src/App.js
import { ResourceAList } from './ResourceA';
import { ResourceBList } from './ResourceB';
const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="MyDataA" list={ResourceAList} />
<Resource name="MyDataB" list={ResourceBList} />
</Admin>
);
I have difficulties trying to pass props to this.props.children
I have a component MainLayout and I would like to pass props to this.props.children but his children are like that :
AppRouter.js
export default class AppRouter extends Component {
render() {
return (
<HashRouter>
<Switch>
<MainLayout>
<Route exact path="/" component={PubsList} />
<Route exact path="/add-pub" component={AddPub} />
<Route exact path="/add-pub/:id" component={AddPub} />
</MainLayout>
</Switch>
</HashRouter>
)
}
}
MainLayout.js : I used React.Children and React.CloneElement to pass props
export default class MainLayout extends Component {
constructor(props) {
super(props);
this.state = {
foo: "foo"
};
render() {
return (
<div className="container" >
<Menu inverted color='teal' fixed="top" style={{ margin: "0", padding: "20px", borderRadius: "0", display: "block" }}>
<Link to="/">
<Header as='h2' content='PandaBar' floated="left" style={{ color: "white", margin: "0" }} />
</Link>
</Menu>
<div className="content" style={{ marginTop: "70px", overflow: "hidden" }} >
{React.Children.map(this.props.children, child => {
return React.cloneElement(child, { foo: this.state.foo })
})}
</div>
</div>
);
}
}
But, once my routes get the props, it appears like this:
<HashRouter>
<Switch>
<MainLayout>
<Route exact path="/" component={PubsList} foo="foo"/>
<Route exact path="/add-pub" component={AddPub} foo="foo"/>
<Route exact path="/add-pub/:id" component={AddPub} foo="foo"/>
</MainLayout>
</Switch>
</HashRouter>
So, how can I pass my props foo to my component PubsList and AddPub with this configuration ?
In the example foo prop is passed to wrong component (Route).
In order to be passed to route components, route component prop should be changed instead:
const withFoo = (Comp, foo) => props => <Comp {...props} foo={foo}/>;
...
render() {
return (
...
{React.Children.map(this.props.children, route => {
return React.cloneElement(route, {
component: withFoo(route.props.component, this.state.foo)
});
})}
...
);
}
While the use of Route render prop as another answer suggest may be more straightforward.
Route does provide a way to pass custom props to the component, by using the render prop instead of the component prop. You pass a function in to the render prop, and that function will get called with the same props that are normally automatically passed when using component. You can then do whatever you want to with those props
An example of this (not including your mainLayout component) would look like this:
<Route render={(routeProps) => (
<PubsList hello="world" {...routeProps} />
)}/>
So using this, you can put arbitrary props onto the components. But then how to let MainLayout dictate what those extra props are? There i think you'll need to modify MainLayout to have a render prop of its own.
render() {
let children = null;
if (this.props.render) {
children = this.props.render({ foo: this.state.foo })
}
return (
<div className="container" >
<Menu inverted color='teal' fixed="top" style={{ margin: "0", padding: "20px", borderRadius: "0", display: "block" }}>
<Link to="/">
<Header as='h2' content='PandaBar' floated="left" style={{ color: "white", margin: "0" }} />
</Link>
</Menu>
<div className="content" style={{ marginTop: "70px", overflow: "hidden" }} >
{children}
</div>
</div>
);
}
And you would use it like this:
<MainLayout render={(layoutProps) => (
<React.Fragment>
<Route exact path="/" render={(routeProps) =>
<PubsList {...layoutProps} {...routeProps} />
}/>
<Route exact path="/add-pub" render={(routeProps) =>
<AddPub {...layoutProps} {...routeProps} />
}/>
<Route exact path="/add-pub/:id" render={(routeProps) =>
<AddPub {...layoutProps} {...routeProps} />
}/>
</React.Fragment>
)} />