React native Accordion - react-native

I'm new to react native and I have been working on a small project where I have use for accordion component after searching I found this
which I tried to implement the thing is how can I make this component reusable cause I have different data for different components. I want to do this without using a JSON file as data source.
for example
getInitialState() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return {
dataSource: ds.cloneWithRows(_.range(25)),
here in datasource i want to pass data for different components
can anyone help

The project that you are using was last updated 2 years ago. You may want to consider using this one instead: https://github.com/oblador/react-native-collapsible

There's also another project named react-native-accordion-wrapper, and you can customize it in any way you'd like, and it's so easy to use:
<Accordion
dataSource={[
{ title: 'header one', child: <Component1 /> },
{ title: 'header two', child: <Component2 /> },
]}
/>
you can also use another child component from this library named AccordionItem if you need more customization.

Related

Set form values with mobx-react-form using object/props

I'm using mobx-react-form and I need to fill a form with default values pulled from an object in my store. Unfortunately, if I try to use FormModel.$("email").set(object.email); inside my component mobx complains that I can't modify observed objects outside of an action and I exceed maxdepth.
Specifically my code looks like this (some details removed for clarity)
import React from 'react';
import ReactDOM from "react-dom"
import { observer } from "mobx-react-lite"
import validatorjs from 'validatorjs';
import MobxReactForm from 'mobx-react-form';
const fields = [{
name: 'email',
label: 'Email',
placeholder: 'Email',
rules: 'required|email|string|between:5,25',
// value: user.email,
}, …
]
const FormModel = new MobxReactForm({ fields }, { plugins, hooks }); //nothing exception here standard plugins/hooks
const UserForm = observer(({open, onClose, object}) => { //My component…object has fields with names email…
FormModel.$("email").set(object.email); //This works fine if I replace object.email with "foo"
return (<MobxInput field={FormModel.$("email")} fullWidth />);
});
export default UserForm;
Yes, I've checked the object has the appropriate fields (it's just a bare object passed in from parent …not even an observable object in this case).
My first approach was to simply put everything inside UserForm and simply fill the values in fields from object but when I do this typing doesn't work in the resulting form (I suspect that mobx is trying to observe an object created inside that observer and that doesn't work).
The problem is I need to use the same form sometimes with data suppled by a user object from my user store and sometimes with blank values to create a new user and I'm kinda stuck about how to do this now.
First of all, you can't do that:
const UserForm = observer(({open, onClose, object}) => {
// This won't really work very well
FormModel.$("email").set(object.email);
return (<MobxInput field={FormModel.$("email")} fullWidth />);
});
Because every time you change value in your input your whole UserForm component also rerenders (because it observes FormModel.$("email") value which just changed) and when it rerenders you instantly change new value to your old value from object. I am not sure why exactly you getting maxdepth error, but there might even be endless loop here as you can see in some cases. Modifying anything like that inside render is usually a bad practice. You need to use useEffect at least, or something like that.
I can't modify observed objects outside of an action
This happens because you need to do all mutations inside actions by default. You can configure it though, if you don't like it:
import { configure } from "mobx"
configure({
enforceActions: "never",
})
But it is better to stick with it, it might catch some unwanted behaviour.
I've made quick Codesandbox example with some of your code, it shows how you can make several forms and pass default values to them:
const UserForm = observer(({ object }) => {
const [FormModel] = useState(() => {
const fields = [
{
name: 'email',
label: 'Email',
placeholder: 'Email',
rules: 'required|email|string|between:5,25',
value: object?.email || ''
}
];
return new MobxReactForm({ fields }, { plugins });
});
return (
<form onSubmit={FormModel.onSubmit}>
<input {...FormModel.$('email').bind()} />
<p style={{ color: 'red' }}>{FormModel.$('email').error}</p>
<button type="submit">submit</button>
</form>
);
});
That is just one of many ways, it all depends what you need in the end.

Dynamic titles with function components

Recently I started rewriting some of my legacy code that used class components to modern hooks. The issue I have is that I used this to set header title like so:
static navigationOptions = ({ navigation }) => ({
title: someVar,
})
Now, I have to do it like so:
MyScreen.navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam('headerTitle'),
}
}
and then
useEffect(() => {
navigation.setParams({
headerTitle: 'Some title',
})
}, [])
Which works fine with static screen titles. But for dynamic titles it does not. It takes a second to update the title, first it renders with empty title. Which is explainable, given the method. It worked perfectly with class components. Is there a better way to do this?
You are setting the params (setParams) passed to the screen and not the options (setOptions) of the screen itself which is what's causing the odd behaviour you are experiencing.
I am not sure what your use case is so I can't tell you which one to use but there are two ways to set the title in react navigation. Either from the navigator using the options parameter or from inside the component using navigation.setOptions take a look at this https://reactnavigation.org/docs/headers

Is it possible to disable expand on certain rows?

I would like to create a DataGrid where only component that have a certain property can be expanded. For example:
comments: [
{ id: 0, author: 'a', text: 'no', responses:[2]},
{ id: 1, author: 'b', text: 'yes' },
{ id: 2, author: 'b', text: 'perhaps' }
]
I would like to display this array, but only first option would be expandable, since it's the only one that has responses. Is there a way of achieving that without rewriting the DataGrid?
Unfortunately no. I would suggest to display an empty state component instead.
Edit
Here is an extract from the documentation:
By default, <Datagrid> renders its body using <DatagridBody>, an internal react-admin component. You can pass a custom component as the body prop to override that default. Besides, <DatagridBody> has a row prop set to <DatagridRow> by default for the same purpose. <DatagridRow> receives the row record, the resource, and a copy of the <Datagrid> children. That means you can create custom datagrid logic without copying several components from the react-admin source.
My suggestion would be to copy the original <DatagridRow> component and add an isExpandable prop accepting a function which will be called with the row record to conditionnaly display the expand button.
You could then use this custom DatagridRow like this:
import MyDatagridRow from './MyDatagridRow`;
const MyDatagridBody = props => <DatagridBody {...props} row={<MyDatagridRow />} />;
const MyDatagrid = props => <Datagrid {...props} body={<MyDatagridBody />} />;
However, as we already have an isSelectable prop, I also suggest to open a new feature request issue on react-admin repository to add an isExpandable prop.

Using FlatList#onViewableItemsChanged to call a Component function

I'm currently attempting to implement a form of LazyLoading using the FlatList component, which introduces a neat little feature called onViewableItemsChanged which gives you a list of all of the components that are no longer on the screen as well as items that are now on the screen.
This is a custom LazyLoad implementation and as such is more complicated than most LazyLoad open-sourced libraries that are available, which is why I'm working on my own implementation. I'm already looked into react-native-lazy-load and others.
Basically, I need to be able to call a function that's part of the component being rendered in the FlatList, I've tried creating a reference to the item rendered in the FlatList and calling it as such, but it doesn't seem to work.
For example:
<FlatList data={...}
renderItem={(item) => <Example ref={(ref) => this[`swiperRef_${item.key}`] = ref}}
onViewableItemsChanged={this.onViewableItemsChanged}
/>
onViewableItemsChanged = ({viewableItems}) => {
viewableItems.forEach((item) => {
const { isViewable, key } = item;
if(isViewable && !this.cachedKeys.includes(key)) {
const ref = this[`swiperRef_${key}`];
if(!ref) return console.error('Ref not found');
ref.startLoading();
this.cachedKeys.push(key);
}
});
}
Now in the <Example /> component I would have a function called startLoading which should be called when a new visible item is brought onto the screen, however the ref never exists.
I was actually doing everything correctly, but I accidently forgot to deconstruct the parameter returned from the renderItem function, so (item) should have been ({ item })
That's all there was to it.

Data Table for react native

Can somebody please suggest any data table component for react native which can add rows dynamically and having editable cells. I am done searching in google. Thanks in advance.
Please find the package available for React Native data table
https://github.com/sussol/react-native-data-table
A React Native data table written purely in JSX with React. ## Installation npm install --save react-native-data-table ## Usage react-native-data-table provides a number of components for constructing a table through composition.
I introduced my own module for this feature from which you will be able to select row easily and much more.
You can use this component like this below
import DataTable, {COL_TYPES} from 'react-native-datatable-component';
const SomeCom = () => {
//You can pass COL_TYPES.CHECK_BOX Column's value in true/false, by default it will be false means checkBox will be uncheck!
const data = [
{ menu: 'Chicken Biryani', select: false }, //If user select this row then this whole object will return to you with select true in this case
{ menu: 'Chiken koofta', select: true },
{ menu: 'Chicken sharwma', select: false }
]
const nameOfCols = ['menu', 'select'];
return(
<DataTable
onRowSelect={(row) => {console.log('ROW => ',row)}}
data={data}
colNames={nameOfCols}
colSettings={[{name: 'select', type: COL_TYPES.CHECK_BOX}]}
/>
)
}
export default SomeCom;
React Native DataTable Component