ShowController with TabbedShowLayout - react-admin

I am actually doing a dashboard using React-Admin, and I discovered the <ShowController> who permits to use Material-UI components inside the Show View.
But my problem comes here, I used a <TabbedShowLayout> and it doesn't work really well. Let me explain :
<ShowController {...props}>
{controllerProps =>
<ShowView {...props} {...controllerProps}>
<TabbedShowLayout {...controllerProps}>
<Tab icon={<AssignIcon />}>
<div>
<TextField source="id" />
</div>
</Tab>
</TabbedShowLayout>
</ShowView>
}
</ShowController>
Here is my actual code, and it displays nothing except the tab with the icon.
But if i remove the <div> component, it works.
I would like to know, if someone ever does that, and made it works.
Thanks by advance

The reason is properties inheritance is not "native", you have to do it manually. react-admin is doing it in each component.
When you insert a div element in a react-admin form or layout, you break this inheritance between Tab and TextField. The div is receiving props from parent Tab, but doesn't forward them to TextField.
One solution is to make a custom wrapper. In your case:
<ShowController {...props}>
{controllerProps =>
<ShowView {...props} {...controllerProps}>
<TabbedShowLayout {...controllerProps}>
<Tab icon={<AssignIcon />}>
<CustomDiv>
<TextField source="id" />
</CusomDiv>
</Tab>
</TabbedShowLayout>
</ShowView>
}
</ShowController>
With
const CustomDiv = ({children, ...props}) => (
<div>
{
React.Children.map(children, child => React.cloneElement(child, {...props, ...child.props}))
}
</div>
);

Related

nativebase tabs dynamic tab flickering

I am using nativebase and dynamic tab rendering flickering.
<Tabs renderTabBar={() => <ScrollableTab />}>
this.state.ifA ?
{<Tab>
<Text>A</Text>
</Tab>: null}
{this.state.ifB ?
<Tab>
<Text>B</Text>
</Tab>: null}
<Tabs>
Like this i have around 7 tab in tabs component which is render dynamically whenever get response from service.
Many times tabs flickering. Anyone have solution for this.
There are two props initialPage and page. Give an index of the tab that you want to render initially in these props. For example you want to render first tab at initially then :
<Tabs renderTabBar={() => <ScrollableTab />} initialPage={0} page={0}>
{ this.state.ifA ? (
<Tab>
<Text>A</Text>
</Tab>
) : null }
{ this.state.ifB ? (
<Tab>
<Text>B</Text>
</Tab>
) : null }
</Tabs>

Display Field in Edit Form

Currently, if I try to place a Field into an Edit form, the field doesn't display at all. There is no errors in the console or the terminal about why it wont.
Example:
<Edit undoable={false} {...props}>
<SimpleForm>
<FormRow>
<TextField source="id"/>
<TextField source="name"/>
</FormRow>
</SimpleForm>
</Edit>
will not display either of these on the page load, it will simply be blank.
Is there any way to use fields in the Edit form?
You need to pass in the record prop (and basePath if its a reference).
The Edit component does not get the record prop so create a form component and it will get passed the record as a prop
eg.
const ProjectEdit: FC<EditComponentProps> = props => {
const classes = useStyles();
return (
<RA.Edit {...props} title={<ProjectTitle />}>
<RA.SimpleForm>
<ProjectForm />
</RA.SimpleForm>
</RA.Edit>
);
};
export const ProjectForm = (props: any) => {
return (
<Box flex={1} mr={{ md: 0, lg: '1em' }}>
<RA.TextInput source="name" fullWidth={true} />
<Typography variant="h6" gutterBottom>
Tasks
</Typography>
<RA.TextField
source="name"
fullWidth={true}
record={props.record}
/>
<RA.ReferenceManyField
label="Tasks"
reference="Task"
target="projectId"
fullWidth={true}
record={props.record}
basePath="/Task"
>
<RA.SingleFieldList fullWidth={true}>
<RA.ChipField source="name" fullWidth={true} />
</RA.SingleFieldList>
</RA.ReferenceManyField>
</Box>
);
};

React-native Input HOC for redux-form loses focus after type a symbol

I'm trying to use redux-form, but, as I read, I need HOC for Input field to replace onTextChange to onChange. I have:
import React from 'react';
import {Input} from 'native-base';
export default function InputField(props) {
const { input, ...inputProps } = props;
return (
<Input
{...inputProps}
onChangeText={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={input.value}
/>
);
};
and use it in my form:
<Item style={{marginTop: 10, width: "100%"}}>
<Field name="login" component={(props) => {
return (
<InputField {...props} keyboardType="email-address" placeholder='E-mail' />
)
}}/>
</Item>
But every time I type key, the field loses focus. Some "experts" recommend use focus() function. But what if I edit text in the middle of it?
Any solutions? Or maybe native-base have compatible textfield component?
It is strange, but it works ))
function InputFieldEmail(props) {
return <InputField {...props} keyboardType="email-address" placeholder='E-mail' />;
}
and use it instead arrow functions
<Field name="login" component={InputFieldEmail}/>
I think it's strange )
You must provide the InputField component as a prop so that its value is constant or put it outside the Item component, when it is inside another, every time the state of the upper level is updated, the lower level is forced to return. to start.
If your Input is inside an Item you are probably using a FlatList and your goal is to put it in the header.
You can try something like this:
<View style={{marginTop: 10, width: "100%"}}>
<Field name="login" component={(props) => {
return (
<InputField {...props} keyboardType="email-address" placeholder='E-mail' />
)
}}/>
</View>
<FlatList
ListHeaderComponent={() => {
<Item>
</Item>
}}
/>
Remember to put the styles that were in Item in View.

How to close react native popup menu?

I'm using your react-native-popup-menu for logout button.
when button clicked the authentication deleted and screen will go to login .
but the menu still left.
How to close this menu when screen switched?
<Menu>
<MenuTrigger>
<Icon
name='more-vert'
color='#fff'
/>
</MenuTrigger>
<MenuOptions>
<MenuOption value={1}>
<Text onPress={() => {
this.props.onLogout()
}}>logout</Text>
</MenuOption>
</MenuOptions>
</Menu>
Originally asked by bexoss on react-native-popup-menu GitHub.
All other answers here will work. Here is another (simpler) solution to the problem depending on what are your requirements.
You are handling logout event in Text component and therefore handlers to close menu are not triggered. Try to pass it to the onSelect property of MenuOption:
<MenuOption onSelect={() => this.props.onLogout()}>
<Text>logout</Text>
</MenuOption>
Note: If you returned false from your handler, menu would not close.
https://github.com/instea/react-native-popup-menu/blob/master/doc/api.md
The API document above states that there is a close() method.
So what you need to do is just declare the ref attribute to access your Menu component directly. E.g "menuRef":
<Menu uref='menuRef'>
<MenuTrigger>
<Icon
name='more-vert'
color='#fff'
/>
</MenuTrigger>
<MenuOptions>
<MenuOption value={1}>
<Text onPress={() => {
this.props.onLogout()
}}>logout</Text>
</MenuOption>
</MenuOptions>
</Menu>
and then you can simply call from any where in your current component: this.refs.menuRef.close();
This approach will also animate the closing.
As mentioned in their documentation, you should use the visible prop in your Menu component.
You will need to adapt your code like this:
<Menu opened={this.state.opened}>
<MenuTrigger onPress={() => this.setState({ opened: true })}>
<Icon
name='more-vert'
color='#fff'
/>
</MenuTrigger>
<MenuOptions>
<MenuOption value={1}>
<Text onPress={() => {
this.props.onLogout()
this.setState({ opened: false })
}}>logout</Text>
</MenuOption>
</MenuOptions>
</Menu>
And I think you will need to define the default state of your component like this:
constructor(props) {
super(props)
this.state = { opened: false }
}
You can also find a full example here

Implementing Footer Tabs in Native React using Native Base

I am creating a native react application using native base for the UI (http://nativebase.io/docs/v2.0.0/components#footerTab). I am using the footerTabs component and my code is as follows
render() {
return (
<Container>
<Header backgroundColor="#ECEFF1">
<Button transparent>
<Icon name='ios-menu' style={{color: 'black'}}/>
</Button>
<Title style={{color:'black'}}>Header</Title>
</Header>
<Content>
<Profile/>
</Content>
<Footer backgroundColor="#212121">
<FooterTab>
<Button backgroundColor="#000" >
<Icon name="ios-person" size={30} color="#900"/>
<Text>Profile</Text>
</Button>
<Button>
<Icon name="ios-search"/>
<Text>Search</Text>
</Button>
<Button>
<Icon name="ios-camera"/>
<Text>Camera</Text>
</Button>
<Button>
<Icon name="ios-apps"/>
<Text>Apps</Text>
</Button>
<Button>
<Icon active name="ios-navigate"/>
<Text>Navigate</Text>
</Button>
</FooterTab>
</Footer>
</Container>
);
}
I have created different JS files for different functionalities such as Profiles,Search,Apps ect.. and have imported them as follows.
import Profile from './Profile';
How do I implement the onPress functionality on the buttons of the footer to change the component in the content tag depending on what was selected?
<Content>
<Profile/>
</Content>
For eg: If I pressed the search button I want the profile tag to be replaced with and similarly the same for the other buttons.
Here FooterTab from native base is not persist actual tab functionality like UITabBar in iOS, its only persist for design. What you need to do is, keep footer tag in all of your component with all four buttons and keep active accordingly. For changing view by selecting any button you need to replace current view by selected one using navigator like:
<Button onPress={()=> { this.props.navigator.replace({id:'component name'}) }}>
and in your navigator component you should define all required components in renderScene method on the basis of id value in route payload. If you want actual functionality as TabBar work then you can use this third party module react-native-tab-navigator. Thanks!
Instead of replacing the content, why don't you have each Button navigate to a new page?
Let's say you're on the Profile tab. You could do something like this:
import FooterWrapper from './FooterWrapper'
<Footer>
<FooterWrapper tab='profile' navigator={this.props.navigator} />
</Footer>
And then in your FooterWrapper (I just handled a case of two tabs):
constructor(props) {
super(props)
this.state = {
profileTab: this.props.tab === 'profile',
searchTab: this.props.tab === 'search',
}
}
navToProfilePage() {
this.props.navigator.push({
id: 'profile',
tab: 'profile',
})
}
navToSearchPage() {
this.props.navigator.push({
id: 'search',
tab: 'search',
})
}
render() {
return (
<FooterTab>
<Button active={this.state.profileTab} onPress={() => this.navToProfilePage()}>
Profile
<Icon name='ios-person' size={30} color='#900' />
</Button>
<Button active={this.state.searchTab} onPress={() => this.navToSearchPage()}>
Search
<Icon name='ios-search' />
</Button>
</FooterTab>
)
}
Ok so here is I how got it I used the renderContent method within the content tags to generate views depending on the state change when the button was clicked.
<Content>
{this._renderContent(this.state.selectedTab)}
</Content>
The selectedTab is a state variable whose state is set using this.setState in the onPress method. The renderContent has an if function that checks the selected tab and returns the appropriate view. I also tried the navigation approach but this seemed cleaner.
_renderContent = (Tab: string,) => {
if(this.state.selectedTab==="Profile"){
return (
<Profile/>
);
}
else if(this.state.selectedTab==="Search"){
}
}