How can I create a modal and render it on the fly? - reactjs

I am building my first app in React Native, I have a ListView that displays a number of items and each one is clickable. My question is when I click on a row I would like a modal to appear above. Similar in IOS to clicking and the adding a subview.
renderRow( rowData ){
return (
<TouchableHighlight onPress={() => this.pressRow()}>
<View style={styles.parcelInfoContainer}>
</View>
</TouchableHighlight>
);
}
Now the problem is I am unsure about what to do in the function called by pressRow in relation to rendering the new component. In IOS I would simply add the view at this point but I am not sure how to go about it in React Native. I understand the idea of components just not how to render a new component on the fly.

If you're going to render the same Modal, in terms of layout, you should define it in render() function. Rendering on the fly is not possible, but you can change the states to make modal show what you like.
onPressRow = (dataFromRow) => {
this.setState({
modalText: dataFromRow.text
})
this.showModal(); //Call your function to show modal
}
render() {
return (
<View>
<FlatList />
<Modal>
<View>
...
<Text>{this.state.modalText}</Text>
</View>
</Modal>
</View>
)
}
However, if you want different layout for each row, you should write a this.getModalContent() like function to determine which layout should be rendered. You should call this function in render => <Modal> {this.getModalContent()} </Modal>
getModalContent = () => {
switch (this.state.modalText) { // define a better logic here.
case 'a':
return ( <Text> hey </Text>)
...
}
}

Related

How to Change Title Attribute in React Native Button Component

I have a few React components that look something like this within an app:
<View style={{margin: 5}}>
<Button title='Enable Headlights' color='#00759A' onClick={doChanges}/>
</View>
I want the Button component to change its color and text whenever its clicked. I was planning on having the function doChanges do this, but I can't figure out a way to modify the title and color attributes. I've seen custom made components that make these kinds of changes in state by calling setState within the component, but Button isn't a component that I made. I tried doing something along the lines of:
var text = "Enable headlights"
function doChanges(text) {
text = "Disable headlights"
}
<View style={{margin: 5}}>
<Button title={text} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
But that doesn't seem to work the way I want it to. I think it's because even though the variable text is changing, the actual component isn't rerendering, so the changes in text never actually appear.
There doesn't seem to be a way to force the component to rerender, and I don't see how there's a way I can modify the title and color attributes of the Button component.
Do I have to make a custom component that's subclassed from Button in order to get the functionality I want? Or is there a specific way for how I'm supposed to go about doing something like this in React?
Any help would be appreciated!
If you are supposed to make changes at the UI level then you should use the state to render the updated view.
like:
If you are using Class Component:
class Demo extends Component {
constructor(props) {
super(props)
this.state = {
buttonText: "Enable headlights"
}
this.doChanges = this.doChanges.bind(this);
}
function doChanges(text) {
this.setState({ buttonText: "Disable headlights" });
}
render() {
return (
<View style={{margin: 5}}>
<Button title={this.state.} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
)
}
}
If you are using Functional Component:
const Demo = props => {
const [buttonText, setButtonText] = useState("Enable headlights")
function doChanges(text) {
setButtonText("Disable headlights");
}
return (
<View style={{margin: 5}}>
<Button title={text} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
)
}
check out more about the state:
https://reactjs.org/docs/state-and-lifecycle.html

How to architect a single modal component accessible by multiple components in React Native?

I'm new to react and I'm having trouble getting my head around the architecture of my app. Here's what I've got:
I use fetch() to get an array of records (records) from the backend. I use a couple of custom components to handle outputting the data. At the end of the tree is a series of touchable components which I need to open a single modal component on press. Here's a simple version of the code:
./app.js
<ScrollView>
{records.map((record, index) => {
return <Post key={index} post={record} />
})}
</ScrollView>
./src/components.js
function Post(props) {
return (
<Child info={props.post} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => ...open modal...}>{props.info}</TouchableHighlight>
)
}
So I can't put my <Modal> in either of the components in components.js, as then I end up with one modal for each record in the database. So I figure I need to put it at the end of the <ScrollView> in app.js, but then how do I open and close it from the <Child> component? I've read that it's possible to pass info back up the chain to a parent component using a callback function, but I'm unsure of how to do that while also passing props down that contain the information of each record.
You can try this logic:
const [modalVisible, setModalVisible] = useState(false);
const handleVisibility = () => {
setModalVisible(!modalVisible)
}
return(
<View>
<YourWrappingModalComponent visible={modalVisible} />
<ScrollView>
{records.map((record, index) => {
return <Post
key={index}
post={record}
handleVisibility={handleVisibility} />
})}
</ScrollView>
</View>
)
To open modal from Child you do:
function Post(props) {
return (
<Child info={props.post} handleParentModal={props. handleVisibility} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => props.handleParentModal()}>{props.info}</TouchableHighlight>
)
}
Using a component like YourWrappingModalComponent where you define your modal is very useful if you use your modal in many places.

React Native FlatList returned from a function or const rerenders from the top when an item is interacted with

I'm trying to put a flatlist checklist into a react native tab view, to do that you need to declare the flatlist as a const so it can be used like . Unfortunately whenever I click on an item at the bottom of the list it pops back to the top and rerenders the list. This problem doesn't occur when a flatlist is rendered directly into your components render function.
I've made a simplified version of what I mean.
https://snack.expo.io/ecO7YYlVZ Is the version where clicking an item pops to the top
https://snack.expo.io/1iQ!ILk4B Is the version where clicking an item doesn't pop to the top.
The only difference between the two is the not working version is like this
const Dat = () => {
return (
<FlatList
style={styles.container}
data={rowsData}
renderItem={this.renderItem}
keyExtractor={extractKey}
/>
);
};
return <Dat />;
Whereas the working version is like this
return (
<FlatList
style={styles.container}
data={rowsData}
renderItem={this.renderItem}
keyExtractor={extractKey}
/>
);
EDIT: I need to have the flatlist in a way that I can add it to a tab using react-native-tab-view: https://github.com/react-native-community/react-native-tab-view
Dat component must be outside of render()
because it reinitializes the whole component and then rerenders that
which cause that behavior
instead, when you create a component outside of the render(), the component itself does not reinitialize only data updated
Your Dat component
const Dat = () => {
return (
<FlatList
style={styles.container}
data={rowsData}
renderItem={this.renderItem}
keyExtractor={extractKey}
/>
);
};
Check the example with the outside of render()
https://snack.expo.io/#jsfit/flatlist

React native re-render causes view to scroll to top - Do my keys change between renders?

I am using react native, redux and flatlist. I render 10 items and then fetch / render the next 10 items on click (load more). My problem is that when I use setState(to increase the page counter) (thats why I dont use setstate in my code) to fetch more items (i use pagination) or ask whether or not Im fetching (true when fetching -> shown loading sign, false when fetched -> showing items), that triggers
a re-render and my view gets scrolled to top.
Ive been reading for a while now and event.preventDefault (as sometimes suggested) doesnt work and I do believe that my problem is the key / parent tree of the corresponding element changes between renders. I do give each item the key of its ID which is always unique. Am I doing it wrong? Or what else could be causing this? Thanks!
class App extends React.Component {
state = {
page : 0
}
componentDidMount() {
this.props.fetchData(0)
this.state.page++;
}
load = () => {
this.props.fetchData(this.state.page);
this.state.page++;
}
renderRow = ({item}) => {
return (
<Item
key={item.id}
coin={item}
/>
)
}
renderButton() {
return <Button
title="Load more"
onPress={this.load}
/>
}
render() {
if (this.props.isFetching) {
return (
<View>
<Text>Loading</Text>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);
}
}
mapStateToProps = state => {
return {
isFetching: state.data.isFetching,
data: state.data.data
}
}
export default connect(mapStateToProps, {fetchData})(App)
Hey change your render method return like below :
return (
{ this.state.isFetching && <View> <Text>Loading</Text> </View>}
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);

Switch view depending on button click

I am new to react native.
My question is pretty simple: my screen contains 5 buttons. Each one opens the same < Modal > component. I need to dynamically change the content of the modal, depending on the button clicked.
For example:
if I click the first button, a text input will be shown into the modal.
If I click the second button, checkboxes will be shown into the modal.
Here's my modal :
<Modal
visible={this.state.modalVisible}
animationType={'slide'}
onRequestClose={() => this.closeModal()}>
<View style={style.modalContainer}>
<View style={style.innerContainer}>
<Text>This is content inside of modal component</Text>
<Button
onPress={() => this.closeModal()}
title="Close modal"
>
</Button>
</View>
</View>
</Modal>
Here I open it :
openModal() {
this.setState({ modalVisible: true });
}
Here I call the function (on button press) :
onPress={() => this.openModal()}
I've heard about using props/children, but I don't know how to use them is this case.
Can anyone please help ?
Here is quick example to show who to render different content based on input you provide.
Modal Content
renderModalContent(type, data) {
switch(type) {
1: {
return (
<View>{..data}</View>
)
}
2: {
return (
<Button>...</Button>
)
}
default: (<CustomComponent data={data} />)
}
}
Modal
<Modal>
<View>
{this.renderModalContent(this.state.type, this.state.modalContentData)}
</View>
</Modal>
Here you decide which view you want to render and pass its data.
openModal() {
this.setState({ modalVisible: true, type: 1, data: {...} });
}
You should modify your Modal component so that it renders the base layout with space for dynamic content to be rendered. The content will be passed in as children, via Props. This will mean the modal is dynamic and will / should support future requirements. Try to avoid the switch case in the modal render suggestion unless you have very specific requirements that are unlikely to change in the future, or if you want to do things the React way.
Then for each variant of your Modal (TextInput, Checkbox etc.) create a new Component that wraps the Modal component and have each button initiate rendering the specific component.
If you're using Redux then you would be creating containers, connecting to Redux and passing dynamic state variables. You don't have to use Redux but the principle is the same.
Here's a very basic example to illustrate my point.
// Basic modal that renders dynamic content
const Modal = props => {
const { children } = props;
render (
<View style={styles.modal} >
{children}
</View>
);
}
// Specific modal implementation with TextInput
const ModalWithTextInput = props => (
<Modal>
<TextInput
value={props.someValue}
/>
</Modal>
)
// Specific modal implementation with Switch
const ModalWithSwitch = props => (
<Modal>
<Switch
value={props.someValue}
/>
</Modal>
)
Then in your component that launches the modals, do something like this...
class MyComponent extends Component {
openTextModal = () => {
this.setState({ modalType: 'text' });
}
openSwitchModal = () => {
this.setState({ modalType: 'switch' });
}
renderModal = (type) => {
if (type === 'text') {
return(<ModalWithTextInput />)
}
if (type === 'switch') {
return(<ModalWithSwitch />)
}
}
render() {
const { modalType } = this.state;
render (
<View>
<View>
<TouchableWithX onPress={this.openTextModal} />
<TouchableWithX onPress={this.openSwitchModal} />
</View>
<View>
{this.renderModal(modalType)}
</View>
</View>
);
}
}
Please note this code has not tested but the principle is sound.

Resources