OnPress change VIEW content - react-native

I want to have two tab buttons on top and some content underneath.
After that, the content I need a View like this :
<Form style={styles.form}>
<Label style={styles.label}>
data 1
</Label>
<Item >
<Input/>
</Item>
<Label style={styles.label}>
Data2
</Label>
<Item>
<Input/>
</Item>
</Form>
When I clicking on the first button, it is active. I need that form to appear.
After that, when I clicking on the second button, I need that form change to:
<Form style={styles.form}>
<Label style={styles.label}>
data 3
</Label>
<Item >
<Input />
</Item>
</Form>
What I'm understanding is that I need a state variable.
state = {showFirst : true, showSecond:false }
and have somewhere a conditional:
if showFirst true, display FORM1
if showSecond true, display FORM2
And
onPress {() => {this.setState{{the state = true)}}
But I am not sure how to bind this together as I'm using React Native for the first time.
Currently what I'm using now is it a good practice?
I set separate states variables for both forms, because another button may be added later.
So I can't only one button:
state = { showForm: true}
showForm?Form1:Form2
onPress={() => {this.setState{{showForm:false)}}
How can I get this to work?

This is a minimum example Component for what you said you were trying to achieve:
import React, {Component} from ‘react’;
import {Button, View} from ‘react-native’;
export default class ExampleComponent extends Component {
constructor(props) {
super(props);
this.state = {
showForm: 0
};
}
render() {
var form;
if (this.state.showForm === 0) {
form = (
<View> INSERT_FORM1 </View>
);
} else if (this.state.showForm === 1) {
form = (
<View> INSERT_FORM2 </View>
);
}
return (
<View>
<Button title=‘Show Form 1’ onPress={() => this.setState({showForm: 0})}/>
<Button title=‘Show Form 2’ onPress{() => this.setState({showForm: 1})}/>
{form}
</View>
);
}
}
You can dynamically choose what content to show based on the Component props and state.
In the example above I used a numerical value to determine what form to show to minimize the amount of state values you would have to track later if the form count expanded.
A switch statement would be a better choice in the event of more available form choices, but I used if-else here for easy of typing for now.

Related

How can I close a Popover programatically with native base?

I am using Native Base Popover.
scenario
As a user,
I can press on the list,
So that I can select a fruit
My problem is I don't understand how to close the <Popover /> from the outside of the component.
Here is my organization
<Formik>
<Popover>
<FlatList>
<Pressable onPress={() => handlePress(item.id)} /> //Banaba
<Pressable onPress={() => handlePress(item.id)} /> //Potato
<Pressable onPress={() => handlePress(item.id)} /> //Ananas
NativeBase offers a useDisclose() hook for handling opening/closing of modals and other popup windows.
That hook provides an isOpen state (as #mainak's answer mentions) as well as onOpen() and onClose() functions to manipulate that state. You can pass these helpers as arguments as-needed into the props of the same name within the Popover component to handle open/close state.
Optionally, you can in addition pass true or false into useDisclose() to override the starting value of isOpen (defaults to false).
Here is an example below for reference.
import React from "react";
import { Popover, useDisclose } from "native-base";
function MyComponent() {
const { isOpen, onClose, onOpen } = useDisclose()
return (
<>
<Button onPress={onOpen}>Open the Popover</Button>
<Popover isOpen={isOpen} onClose={onClose}>
<Popover.Content>
<Popover.Arrow />
<Popover.CloseButton />
<Popover.Header>My Popover Title</Popover.Header>
<Popover.Body>You can place the content of your popover inside the body.</Popover.Body>
<Popover.Footer>
<Button onPress={onClose} variant="ghost">Cancel</Button>
</Popover.Footer>
</Popover.Content>
</Popover>
</>
)
}
can you try isOpen prop in Popover tag and have it as a state value like
const [isOpen, setOpen] = React.useState(true);
...
<Formik>
<Popover isOpen={isOpen}>
<FlatList>
...

How to create a custom record action button inside a List component with React-Admin?

I'm a totally newbie with React and React-Admin. IMHO, I'm trying to achieve something simple that many people must have already done but I cannot find any kind of tutorial anywhere.
I'd like to add another button to the list of action buttons (show/edit) within each row in a <List> component. This button would archive the record.
My last try looks like the code below.
import React from 'react';
import {
Datagrid,
EmailField,
List,
TextField,
ShowButton,
EditButton,
DeleteButton,
CloneButton,
} from 'react-admin';
import { makeStyles } from '#material-ui/core/styles';
import ArchiveIcon from '#material-ui/icons/Archive';
const useRowActionToolbarStyles = makeStyles({
toolbar: {
alignItems: 'center',
float: 'right',
width: '160px',
marginTop: -1,
marginBottom: -1,
},
icon_action_button: {
minWidth: '40px;'
},
});
const ArchiveButton = props => {
const transform = data => ({
...data,
archived: true
});
return <CloneButton {...props} transform={transform} />;
}
const RowActionToolbar = (props) => {
const classes = useRowActionToolbarStyles();
return (
<div className={classes.toolbar}>
<ShowButton label="" basePath={props.basePath} record={props.record} className={classes.icon_action_button}/>
<EditButton label="" basePath={props.basePath} record={props.record} className={classes.icon_action_button}/>
<ArchiveButton {...props} basePath={props.basePath} label="" icon={<ArchiveIcon/>} record={props.record} className={classes.icon_action_button} />
<DeleteButton basePath={props.basePath} label="" record={props.record} className={classes.icon_action_button}/>
</div>
);
};
export const UserList = props => {
return (
<List
{...props}
sort={{ field: 'first_name', order: 'ASC' }}
>
<Datagrid>
<TextField source="first_name"/>
<TextField source="last_name"/>
<EmailField source="email"/>
<RowActionToolbar/>
</Datagrid>
</List>
)
};
Obviously, this code does not work because the <CloneButton> component get rid of the id the record. Moreover, except if I did something wrong - which is totally possible -, it makes a GET request to a create endpoint.
I'm using different routes in my dataProvider (The back end is using Django and Django rest framework). I want to send a PATCH to the detail endpoint, like the <Edit> component does.
I also tried with a <SaveButton>, but it fails too.
Uncaught TypeError: Cannot read property 'save' of undefined
at useSaveContext (SaveContext.js:23)
I guess the <SaveButton> must be within a <SimpleForm>?
I'd like the save behaviour of the <DeleteButton>, i.e. update the record from the list, display the notification that the record has been archived (with the Undo link), send the request to the back end, refresh the list.
Any guidance, directions would be very appreciated.
I don't know that this is a full answer, but felt like more than a comment...
You are trying to archive the existing record, not create a whole new record, right? CloneButton is supposed to be used to create a new record with a new ID (which is why your ID is going away), so you don't want to us it here. note that I've never used CloneButton. it is not fully documented so I could be wrong about its use.
I am thinking that you should use the useRecordContext hook within your Archive button to pull in all of the record's data, including the id; read this little section: https://marmelab.com/react-admin/Architecture.html#context-pull-dont-push
And I don't think transform is what you're looking for here. You will need to use one of the dataProvider hooks, i'm assuming useUpdate: https://marmelab.com/react-admin/Actions.html#useupdate
//first create component
const MyButton = (props: any) => {
const [sendEmailLoading, setSendEmailLoading] =
React.useState<boolean>(false);
const record = useRecordContext(props);
const sendEmail = (id: Identifier) => {
setSendEmailLoading(true)
dataProvider.sendEmail(
"notifications", { id: id })
.then(({ data }: any) => {
if (data && data.status == "success")
notify('Email send success', { type: 'success' });
setSendEmailLoading(false);
refresh();
});
};
return (
<ButtonMUI color='primary' size="small" onClick={() => {
sendEmail(record.id) }}>
{
!record.publish &&(
!sendEmailLoading ? (
translate('resources.notifications.buttons.send')
) : (
<CircularProgress size={25} thickness={2} />
)
)
}
</ButtonMUI>
)
}
//and second add to datagrid list
<Datagrid>
<NumberField source="id" />
<TextFieldRA source="subject" />
<DateField source="date" />
<BooleanField source="publish" />
{/* <EditButton /> */}
<ShowButton />
<MyButton />
</Datagrid>

React-native Formik setFieldValue

Here is a simplified version of my code.
Notice the setFieldValue_ and this.setFieldValue_ = setFieldValue;
This code works fine, I'm able to get the output when submit button is clicked.
I'm actually wondering if this is the right way to do it? If not, can you point me to the right direction? Also what is this method called? (assigning class variable to some function and use it within another function)
class MyComponent extends React.Component {
setFieldValue_;
someFunction() {
this.setFieldValue_("name", value);
}
render() {
return (
<Formik
initialValues={{
something: ""
}}
onSubmit={values => console.log(values)}
>
{({
setFieldValue,
}) => {
this.setFieldValue_ = setFieldValue;
<ThirdPartyCustomComponent onChange={this.someFunction} />
}}
</Formik>
}
}
I would personally have the onChange simply call formik set field value there and then rather than using different functions. Strictly speaking you don't want to set the value like that because every re-render is setting the value again.
I would also recommend looking at custom formik inputs using the useField hook - https://jaredpalmer.com/formik/docs/api/useField. This will allow you to write a small wrapper around your third party component and formik. Noticing you have used a class based component you may want to do some short reading into react hooks before throwing yourself into using useField.
Docs example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<label>
{label}
<input {...field} {...props} />
</label>
{meta.touched && meta.error ? (
<div className='error'>{meta.error}</div>
) : null}
</>
);
};

How to update checkbox appearance or state in child

I have searched about but not found a solution (I'm a beginner, so...) despite a few posts such as:
Use modal to take user checkbox input and return data back to parent component
I have a parent with a number of checkboxes. Depending on the state of a switch I insert either child1 or child2 into the parent.
Each child has 4-5 checkboxes, not just one.
I have figured out that the best way (for me) is to have the state for the checkbox values and the checkbox visual state (as in a showing as checked or unchecked) should be handled in the parent with props. I don't want to use redux.
I am having issue with the checkbox not showing as checked in the child, even though the value is being passed back. I had a toggle function in each child but that just meant that while it worked, it was one step behind, showing unchecked when checked and vice versa.
My parent:
import React, { Component } from "react";
import { StyleSheet, Text, View, Image, ImageBackground, ScrollView, Switch, Platform } from "react-native";
import CircleCheckBox, { LABEL_POSITION } from "react-native-circle-checkbox";
import Child1 from "../components/Child1";
import Child2 from "../components/Child2";
// Function called when checkbox is toggled
onchange(checkedBoxName, value){
if(this.state[checkedBoxName]==false){
this.setState({
[checkedBoxName]: true,
})
Let myResult = null,
// Do math and stuff with the value argument and set outputText
this.setState({outputText: myResult});
}
class ParentScreen extends Component {
constructor(props) {
super(props);
this.state = {
outputText: "",
///checkbox states that are in the parent here
//followed by states for child1 checkbox
Checked10: false,
Checked11: false,
Checked12: false,
Checked13: false,
// then child2 checkboxes
Checked14: false,
Checked15: false,
Checked16: false,
Checked17: false,
}
render(){
return(
// bunch of views and checkboxes etc in the parent like this:
<View style={[styles.checkbox, styles.checkbox2]} >
<CircleCheckBox outerSize={28} filterSize={26} innerSize={14}
outerColor={"#ff0000"} filterColor={"#FFF"} innerColor={"#ff0000"}
style={styles.checkbox} checked={this.state.checked1} onToggle={(checked) => this.onchange("checked1", 1)} />
</View>
// etc
// Then choosing and displaying my child component:
<View>
{switchChild ? <Child1 changeChild1 ={this.onchange.bind(this)} /> : <Child2 changeChild2 ={this.onchange.bind(this)} />}
</View>
);
}
Then in my child component:
import React, { Component } from "react";
import { View, Text, StyleSheet, Image } from "react-native";
import CircleCheckBox, {LABEL_POSITION} from "react-native-circle-checkbox";
class PL extends Component {
// no checkbox states here
render() {
return(
<View style={[styles.checkbox, styles.checkbox2]} >
<CircleCheckBox outerSize={28} filterSize={26} innerSize={14} outerColor={"#ff0000"} filterColor={"#FFF"} innerColor={"#ff0000"} style={styles.checkbox} checked={this.state.checked10} onToggle={(checked) => { this.props.changeChild1("checked10", 10) }} />
</View>
<View style={[styles.checkbox, styles.checkbox2]} >
<CircleCheckBox outerSize={28} filterSize={26} innerSize={14} outerColor={"#ff0000"} filterColor={"#FFF"} innerColor={"#ff0000"} style={styles.checkbox} checked={this.state.checked11} onToggle={(checked) => { this.props.changeChild1("checked11", 11) }} />
</View>
<View style={[styles.checkbox, styles.checkbox2]} >
<CircleCheckBox outerSize={28} filterSize={26} innerSize={14}
outerColor={"#ff0000"} filterColor={"#FFF"} innerColor={"#ff0000"} style={styles.checkbox} checked={this.state.checked12} onToggle= {(checked) => { this.props.changeChild1("checked12", 12) }} />
</View>
//etc
);
}
}
So, what I need is a way to show that each child checkbox is checked when it's clicked, particularly when there are multiple checkboxes in the child. Since it's more than one checkbox, I can't just hard code in a particular state. I assume I use a variable in the "checked={this.state.checked11}" line (like: checked={this.state[aVarWithCheckboxName]} or maybe there is another way to do this?
Will this need to pass multiple functions to work?
Ok. So after a lot more reading and pulling out bits here and there, I fixed the problem. But I think it might be a bit of overkill so would like to hear how to avoid sending the whole state to the child, and just send, or reference to, a collection of state items.
Got rid of all state in child components. State for all checkboxes were put into the parent along with all of the functions that processed the data.
Parent:
Did not need ".bind(this)" thanks to ES6.
<View>
{switchChild ? <Child1 changeChild1 ={this.onchange} {...this.state} /> : <Child2 changeChild2 ={this.onchange} {...this.state} />}
</View>
Child:
<View style={[styles.checkbox, styles.checkbox2]} >
<CircleCheckBox //all the styling goes here
checked={this.props.checked10} onToggle={(checked) => { this.props.changeChild1("checked10", 10) }} /> }} />
</View>
So now no matter how many checkboxes I have, I can access the parent state with the individual name of the checkbox. Sending back the data to run the onchange function remains the same.
If there is a better way of handling this (targeting the checked item in the parent state), let me know.

How do I bind a local function when I pass a rendered view interacting with that function to a prop in react-native?

I'm passing a render to the Accordion element in native-base using the renderContent prop. The render contains two buttons, which, when pressed, run functions that are local to the current component. Unfortunately those functions are not available once it has been actually rendered.
How do I bind the functions properly so that when pressed, the correct functions are referenced?
I'm using the most modern stable releases of react-native, native-base, and I'm running this through expo for testing.
Here's the documentation on native-base:
http://docs.nativebase.io/Components.html#accordion-custom-header-content-headref
Accordion:
<Accordion
dataArray={ this.state.websites }
renderContent={ this._renderAccordionContent }
/>
renderContent:
_renderAccordionContent(content) {
return (
<Button
onPress={() => this.openSite(content.path)}
>
<Text>Open</Text>
</Button>
<Button
onPress={() => this.editSite(content.key)}
>
<Text>Edit</Text>
</Button>
)
}
When the buttons are pressed, the expected results are that the functions are run.
The actual results are that when the buttons are pressed, these errors are populated:
_this2.openSite is not a function.
_this2.editSite is not a function.
Thank you for any help.
Check out this excellent article that shows several different ways of binding your functions https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56
Here is an example of binding it in the constructor of your component that uses the Accordion component. It is by no means the only way of binding the functions. The above article gives 5 different ways of doing it.
class MyComponent extends Component {
constructor(props) {
super(props);
this.openSite = this.openSite.bind(this);
this.editSite = this.editSite.bind(this);
}
// I am assuming you have written your functions like this and not as arrow functions
openSite (path) {
...
}
editSite (key) {
...
}
_renderAccordionContent(content) {
return (
<Button
onPress={() => this.openSite(content.path)}
>
<Text>Open</Text>
</Button>
<Button
onPress={() => this.editSite(content.key)}
>
<Text>Edit</Text>
</Button>
)
}
render() {
...
<Accordion
dataArray={ this.state.websites }
renderContent={ this._renderAccordionContent }
/>
...
}
}