How to achieved multiple nested screens - react-native

Having trouble with my StackNavigator with two another screens in the nested, below are the nested stack I want to achieve.
Items
-Help (already achieve this nested)
-Help Details
-Topup
-Topup Details
-Services (I want to achieve something like this);
-Service name (Babysitting)
-service name details (Babysitting details)
This is ony my DashboardNavigator
function DashboardNavigator() {
return (
<StackDashboard.Navigator initialRouteName="Dashboard">
<StackDashboard.Screen name="Topup" component={TopupStack} options={navOptionHandler} />
<StackDashboard.Screen name="Services" component={ServicesStack} options={navOptionHandler} />
<StackDashboard.Screen name="BabySitting" component={BabySittingStack} options={navOptionHandler} />
</StackDashboard.Navigator>
)
}
function BabySittingStack() {
<StackBabySitting.Navigator initialRouteName="BabySitting">
<StackBabySitting.Screen name="BabySitting" component={BabySitting} options={navOptionHandler} />
<StackBabySitting.Screen name="BabySittingDetails" component={BabySittingDetails} options={navOptionHandler} />
</StackBabySitting.Navigator>
}
function ServicesStack() {
return (
<StackServices.Navigator initialRouteName="Services">
<StackServices.Screen name="Services" component={Services} options={navOptionHandler} />
<StackServices.Screen name="BabySitting" component={BabySittingStack} options={navOptionHandler} />
</StackServices.Navigator>
)
}
This will be my screen that will go to my details screen
<SafeAreaView>
<TouchableOpacity style={styles.item} onPress={() => navigation.navigate('BabySittingDetails')}>
<Text style={styles.title}>BabySitting</Text>
<Image
style={styles.imageItem}
source={IMAGE.ICON_NEXT} resizeMode="contain" />
</TouchableOpacity>
</SafeAreaView>

I'm not sure this is your problem but BabySittingDetails is accessible in BabySittingStack. BabySittingStack and ServicesStack are at the same level, so you can't access babySittingdetails like that. You can test to move your BabySittingComponent inside your ServicesStack.
But I don't think you should get this kind of error with your current navigation architecture. If it does not solve your problem then please show us your BabySittingDetails component entirely and eventually reproduce your problem on a snack expo. You can use this boilerplate to reproduce your problem (ios is working fine as well as web, android have some trouble with stylesheet i don't know why).

Related

TabbedForm not highlighting which tab has an error

I have a TabbedForm with 2 tabs, each tab having a single required() field. When I submit this form and the validation fails, I expect the unfocussed tab(s) to indicate that there is an error with a field within the tab (e.g. with a red underline or red text).
This appears to be working fine in a react-admin demo (https://marmelab.com/react-admin-demo/#/products/126) however even after looking a the source code for this example (https://github.com/marmelab/react-admin/blob/master/examples/demo/src/products/ProductEdit.tsx), I cannot seem to replicate the same functionality in my project.
I have the following code:
const App = () => {
const dataProvider = jsonServerProvider(
"https://jsonplaceholder.typicode.com"
);
return (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={ListGuesser} edit={EditForm} />
</Admin>
);
};
export const EditForm = (props: EditProps) => {
return (
<Edit {...props}>
<TabbedForm>
<FormTab label="Tab 1">
<TextInput source="name" validate={required()} />
</FormTab>
<FormTab label="Tab 2">
<TextInput source="username" validate={required()} />
</FormTab>
</TabbedForm>
</Edit>
);
};
Image showing Tab 2 selected and is valid and there is a validation error on Tab 1, but no highlight on Tab 1 to tell the user that this is the Tab that has the error.
There has been a similar question asked here (Show Tab Form Validation For Inputs Not Direct Children Of <FormTab>) but the resolution does not apply to my problem.
Is there something I'm missing here?
plz check the demo source code: https://github.com/marmelab/react-admin/blob/master/examples/demo/src/products/ProductEdit.tsx, it's using validate function:
<RichTextInput source="description" label="" validate={req} />
and the "req" is defined at line 86:
const req = [required()];
I've encountered same problem, and solve it by using the way (validation function) of demo source code. HTH

How do I implement Algolia Search filtering in a React Native Modal component?

I am using Algolia search in my React Native app, following the instructions in guides/building-search-ui/going-further/native/react
I have created a RefinementList as per the instructions, and this works fine when placed directly with the app, as per
<InstantSearch
searchClient={searchClient}
indexName={THE_INDEX}
root={root}
>
<RefinementList attribute="brand.name" />
<SearchBox />
<InfiniteHits />
</InstantSearch>
When this runs and I enter a search term I happily get a list of my faceted brand names. But I need this to work within a modal. So following Algolia's instructions I have created a Filters component and use my RefinementList from within that instead:
<InstantSearch
searchClient={searchClient}
indexName={THE_INDEX}
root={root}
searchState={this.state.searchState}
onSearchStateChange={this.onSearchStateChange}
>
<Filters
isModalOpen={this.state.isModalOpen}
toggleModal={this.toggleModal}
indexName={THE_INDEX}
searchClient={searchClient}
searchState={this.state.searchState}
onSearchStateChange={this.onSearchStateChange}
/>
<SearchBox />
<FilterButton
onFilter={this.toggleModal}
/>
<InfiniteHits />
</InstantSearch>
However the modal is always empty. The console shows that when the RefinementList is simply a child of the InstantSearch then its items array gets populated, but when the RefinementList is invoked within the Filters component then it's empty. The Filters component is exactly as per Algolia's docs, apart from the fact I pass in the indexName as a prop. I've checked in the React Native debugger and the RefinementList has access to the same context, the searchState is being set, and onSearchStateChange is being invoked correctly. The indexName is correct.
What am I doing wrong?
To fix this I had to add a 'virtual' SearchBox, ala
const VirtualSearchBox = connectSearchBox(() => null)
which is used as a child of the InstantSearch component in the Filters component, making Filters more like
const Filters = ({
isModalOpen,
searchState,
searchClient,
toggleModal,
onSearchStateChange,
indexName
}) => (
<Modal animationType="slide" visible={isModalOpen}>
<SafeAreaView>
<InstantSearch
searchClient={searchClient}
indexName={indexName}
searchState={searchState}
onSearchStateChange={onSearchStateChange}
>
<VirtualSearchBox />
<RefinementList attribute="brand" />
<TouchableOpacity style={styles.closeButton} onPress={toggleModal}>
<Text style={styles.closeButtonText}>Close</Text>
</TouchableOpacity>
</InstantSearch>
</SafeAreaView>
</Modal>
)
Inside the modal the searchState is always falsy because the SearchBox is not mounted.
Each time a request happens inside the modal the emtpy state will be provided.
The refinements inside the searchState are not enough to apply a refinement on the search parameters. All of the widgets associated with the refinement have to be mounted (ref: Search State docs).

React-Admin | Bulk-Select with ReferenceField

In our application, we're trying to use Datagrid within ReferenceField to create/modify/delete related records, as shown in https://marmelab.com/blog/2018/07/09/react-admin-tutorials-form-for-related-records.html
All the functionality shown in the tutorial works well, except the bulk-actions added in a recent react-admin update. Clicking these checkboxes gives
Uncaught TypeError: _this.props.onToggleItem is not a function
I believe this is because the onToggleItem prop is normally provided by the List component, however in this application, Datagrid doesn't have a parent List component.
Adding a List component between ReferenceManyField and Datagrid allows bulk select/delete to work after some changes to the style, however this causes another issue: the current displayed page (i.e. records 1-10, 11-20, etc) is stored per-resource in the store, and so it is possible to have a situation where the store says we're on page 2, and displays page 2, which is empty because there are only enough related items to fill one page.
Am I missing something here? Or is bulk-select inside ReferenceManyField not possible at the moment?
export const NetworksShow = (props) => (
<Show title={<NetworksTitle />} actions={<NetworksShowActions />} {...props}>
<ReferenceManyField addLabel={false} target="ipid" reference="acl-network">
<List style={{ margin: '0px -24px -16px -24px' }} {...props} actions={<NetworkACLCardActions ipid={props.id}/>} filter={{ ipid: _.has(props, 'id') ? props.id : undefined }}>
<Datagrid hasBulkActions={true}>
<ReferenceField label="Network" source="ipid" reference="networks">
<TextField source="name" />
</ReferenceField>
<TextField label="URL" source="url" />
<BWChip label="Action" source="wb" />
<EditButton />
<DeleteButton />
</Datagrid>
</List>
</ReferenceManyField>
</Show>
);
As a side-effect of https://github.com/marmelab/react-admin/pull/2365, it is now possible to use ReferenceManyField -> List -> Datagrid in the way described in the question.
For example, we're now doing the following:
<ReferenceManyField addLabel={false} target="groupid" reference="users">
<List
style={{ margin: '0px -24px -16px -24px' }}
filter={{ groupid: id }}
{...props}
>
<Datagrid hasBulkActions>
<LinkField label="Name" source="name" />
<LinkField label="Username" source="email" />
<FlexibleBooleanField label="Email" source="allowemail" />
<ACLChip label="User Access" source="aclid" />
</Datagrid>
</List>
</ReferenceManyField>
Bulk actions works with the above, and any issues with pagination are avoided as react-admin now checks and modifies pagination if nothing appears on the current page.
As I've understood from the documentation, Datagrid is just an iterator "dumb component".
It just "shows" things that the parent - usually List (connected component) or in your case ReferenceManyField - element previously has fetched.
Thus I think that BulkActions can only be functional when provided by a List element.
For the second part of your issue, Lists should be used top-level and not within other elements that's why it breaks your pagination.
I implemented "DumbList" which takes data from parent component instead of loading it itself. This solves the problem:
import React from 'react';
import { ListController } from 'ra-core';
import { ListView } from 'ra-ui-materialui/esm/list/List';
export const DumbList = props =>
<ListController {...props}>
{controllerProps => {
let { data } = props
const ids = Object.keys(data || {})
const total = ids.length
return <ListView
{...props}
// This is important, otherwise bulkActionsToolbar ends up on very top of the page
classes={{ card: 'relative' }}
{...Object.assign(controllerProps, { data, ids, total })} />
}}
</ListController>

How can I delete a single item from a list in React Native?

So that's what my render list looks like:
renderEvent(){
return this.props.eventList.map(listedEvent=>
<CardSection key={listedEvent._id}>
<EventListItem>
{listedEvent.eventName}
</EventListItem>
<DeleteButton
onClick={this.deleteEvent(listedEvent._id)}
key={listedEvent._id}
/>
</CardSection>
);
}
and here the rendering of the whole list
render(){
if (this.props.eventList !== undefined) {
return(
<Card>
{this.renderEvent()}
</Card>
)
}else{
return(
<View>
<Spinner/>
</View>
)
}
}
}
So far so good, the list and the delete button appear correctly, yet when I try to delete one line, it addresses all. I created my event handler which for now, only logs the passed id.
deleteEvent(eventID){
console.log(eventID)
}
Yet when called, it logs all the _ids on the list. What am I doing wrong? How can I make sure I'm passing one single id of the list item I'm clicking on?
Thank you!
Problem is that you are rather than passing a deleteEvent function to onClick prop you are executing it. This causes to deleteEvent fire for each item while rendering.
You can sort this out by changing this,
onPress={this.deleteEvent(listedEvent._id)}
to this
onPress={() => this.deleteEvent(listedEvent._id)}
This will also assure that deleteEvent is bind with this.

React-native: How to control what keyboard pushes up

The structure of the app is fairly simple: A searchbar, a listview and react-native-tabs at the bottom. The problem: If I click on the searchbar on Android it pushes the whole app up, so I see the tabs directly over the keyboard. But on iOS the keyboard overlays the whole app, without pushing anything up. Is there any way to control that?
I'm happy to share code / screenshots if the question isn't clear.
Thanks
edit:
<View style={{flex:1, backgroundColor:'#f2f2f2'}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderSearchResults.bind(this)}
style={styles.listView}/>
<KeyboardAvoidingView
style={styles.addQuestionBar}
behavior={'position'}>
<Text>
Don't see your question?
</Text>
<TouchableOpacity>
<Text>
Add it
</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
</View>
Set android:windowSoftInputMode="adjustPan" in your manifest file, and it will work as you expect.
E.g.
<application
android:name=".MainApplication"
android:allowBackup="true"
...
<activity
android:name=".MainActivity"
android:label="#string/app_name"
...
android:windowSoftInputMode="adjustPan">
...
</activity>
...
</application>
For those using Expo
#J KW's answer is correct but if you're using Expo you will have to implement it differently.
Expo uses different configuration terms. In your app.json you have to set the configuration key
"softwareKeyboardLayoutMode":"pan" in your android object.
Your file might look something like:
{
"expo": {
"name": "App",
...
"android":{"softwareKeyboardLayoutMode": "pan"},
...
}
}
Note: If you are receiving a "should NOT have additional property" error, it's likely because you don't have the updated Expo SDK (v.038). Please update your Expo version.
Documentation: https://docs.expo.io/workflow/configuration/
There is new Component called KeyboardAvoidingView from React Native not documented yet but i already use it in my project and its very useful
Code Example:
'use strict'
import { ... , KeyboardAvoidingView } from 'react-native'
class Test extends Component {
constructor () {
super()
this.state = {
behavior: 'position'
// there is three ways to adjust (position , height , padding )
}
}
render(){
return (
<View>
<KeyboardAvoidingView behavior={this.state.behavior}>
<TextInput
style={style.PhoneNumberInput}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
</KeyboardAvoidingView>
</View>
)
}
}
module.exports = Test
and for more details you can check KeyboardAvoidingViewExample
EDIT:
Sorry i just got the wrong idea from the question i think what you are trying to do is stop the android keyboard from pushing the application up here is component that allow you to choose between (Pan, Resize, Nothing) in android
react-native-android-keyboard-adjust
Use android:windowSoftInputMode="adjustResize".
KeyboardAvoidingView and other keyboard-related components don't work quite well if you have "adjustPan" set for your android:windowSoftInputMode in AndroidManifest.xml.
Instead, you should use "adjustResize" and have a wonderful life.
I had "adjustPan" and tried using KeyboardAvoidingView and KeyboardAwareScrollView... nothing worked.
Now, with "adjustResize", i'm not using any keyboard-related component and my android app works. (I might have to use KeyboardAvoiding view on iOS, but it will work out of the box.)
#binkie's answer worked for my expo (Version 44.0.0) app with a slight change.
In app.json,
"android":{"softwareKeyboardLayoutMode": "pan"}
In the screen, margin bottom value equal to height of the bottom tab like so
<ScrollView mb="70px">...</ScrollView>
****1) modify the AndroidMainfest.xml in
android/src/main/AndroidMainfest.xml
u can solve the issue by changing the
$ android:windowSoftInputMode="adjustResize";
to
$ android:windowSoftInputMode="adjustPan";
the problem will be resolvede****