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

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).

Related

How to implement this lib in a Custom Flatlist

i have a flatlist like image bellow. The code is this.
How to implement the pop menu in this case?
renderList = () => (
<FlatList
data={this.props.contracts.data}
keyExtractor={item => String(item.id)}
renderItem={({ item }) => <ListItem onPress={() => {}} listItem={item} />}
/>
);
There is nothing special about rendering popup menu inside of flat list. Simply put it into your ListItem component.
Ad "in most cases you should not have more menu providers in your app (see API documentation). In other cases use skipinstacecheck prop" warning. Normally (like 98% of cases) you should not have multiple MenuProviders in your application. If you put it inside of ListItem, it will be rendered multiple times. Just use one MenuProvider on the top of your applications - see main README:
Wrap your application inside MenuProvider and then simply use Menu
component where you need it.
And there are plenty of examples where you can look into, e.g. https://github.com/instea/react-native-popup-menu/blob/master/examples/InFlatListExample.js

How to open and close Shopify Polaris Modal using App-Bridge

I'm using Shopify Polaris #3.4.0 and App-Bridge #1.0.3. Also used shopify-node-app as a starting.
I initialize app-bridge by passing apiKey and shopOrigin values like so:
<AppProvider apiKey={apiKey} shopOrigin={shopOrigin}
<Switch>
<Route exact path='/admin' component={Dashboard} />
</Switch>
</AppProvider>
In my Dashboard component I have a Modal component:
<Modal
src='https://somewhere/',
title='title',
open={modalActive}
/>
I open the modal by setting the state to true for modalActive like so:
this.setState({modalActive: true});
Shopify Polaris Modal uses an iframe when src is used and in that view I have a submit form. I would like to close the Modal when the form is submitted. I have tried dispatching an action to close the modal using app-bridge, but honestly I'm confused and these are all recent releases so any help is appreciated.
Long story short pass a function to the Modal primaryAction prop. The core of this is child to parent component state changes. So more of a ReactJS question.
Alright, had a similar issue and it's a little embarrassing to admit this but hey if it saves you a half and hour then great.
Shopify's Modal docs use active to keep the state of the externally triggered modal. But don't forget to use open as the actual prop to the <Modal> component:
<Modal
open={active}
...rest of props
/>
...content
</Modal>

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>

React-Native - pass data/function to component

I've got this function inside parent component:
toggle = () => {
console.log('123');
}
This is parent component, and I want pass this function (or be able to use this function actually) in one of my child components (the 3rd in hierarchy).
I'll try to describe it:
<ScrollView
showsVerticalScrollIndicator={false}
>
<Category title={'news'} />
<Category title={'entertainment'} toggleDropZone={this.toggleDropZone} />
</ScrollView>
The Category component export a list of items:
<Item title="1" />
<item title="2" />
So, I want from my Item component to use the toggle function. I know the method of using props toggle={this.toggle} but I don't want to pass it in each child because its not practical.
There is any other options to pass this data to my child components or alternatively other way to access my main parent from my child component?
Thanks a lot.

React Native - Reuse same component instance instead of a rerendered one?

So I have a custom tab component based solely on activeTab state:
<View style={styles.contentDisplayArea}>
{ this.state.activeTab == "Children" && <Children /> }
{ this.state.activeTab == "Adults" && <Adults /> }
</View>
The <Children /> and <Adults /> components are imported components which have nested forms. When Adults is active I get the <Adults /> component, I fill out the adult name and age with TextInput fields, then I click the Children tab and get the <Children /> component. When I go back to the Adults tab, I expect the same fields I filled out to be there, but it seems like the component is back to its initial state instead, with the fields I filled out emtpy.
So, how can I reuse the same component (instance/reference/cached) I filled out already instead of getting a freshly rendered empty one?