I use a render prop in react (native). I want to pass a button from Parent to child where the onClick method from Button shoud be triggered from Child. I dont know what Im doing wrong but the function inside child is never called. I looked at examples but cant figure out whats wrong. Any help would be appreciated!!
const Parent = () => {
return (
<View>
<Selector
render={({handleSelect}) => {
return <Button
title="select"
handleSelect={handleSelect}
/>
}}
/>
</View>
)
}
const Child = ({render}) => {
const handleSelect = (input) => {
console.log("helllooo") //is never called
};
return (
<View>
{render({handleSelect: handleSelect})}
<View><Text>other content here...</Text></View>
</View>
)
};
Seems like you have to pass handleSelect on onPress on Button component
Related
everyone. I'm new in this new tecnology and I'm having a little problem with modals.
I'm programming a modal that only shows when a condition is true, in the first click it's all ok, but in the second the modal won't show, then when I click again, the window appears again, and continue successively.
The code works as follow: I have 3 states, each represent a different text to show in the modal. Then, I have a modal, that recieves a message (to print) and the state to identifies what state i need to show. And finally, in the main, I have a condition who calls the modal and show it ONLY if the state is true.
I checked the states with console.log and the only issue i find is that in some cases, the state is set in true, when is false, but i don't know why!
Thanks for read!
Here i detach part of the code.
Declare of the states in the main
const [modalVisibleEnTermino, setModalVisibleEnTermino] = useState(false)
const [modalVisibleFueraTermino, setModalVisibleFueraTermino] = useState(false)
const [modalVisibleRefuerzoTermino, setModalVisibleRefuezoTermino] = useState(false)
Condtition in main to show the apropiate state of modal
const setModal = () => {
vacunas ===2 && (days>6 && days<150) ? setModalVisibleEnTermino(true) : setModalVisibleFueraTermino(true)
if(vacunas == 3)
setModalVisibleRefuezoTermino(true)
}
Function to reset the states after showing modal (IDK if its ok)
const restoreModals = () => {
if (modalVisibleEnTermino) setModalVisibleEnTermino(false)
if (modalVisibleFueraTermino) setModalVisibleFueraTermino(false)
if (modalVisibleRefuerzoTermino) setModalVisibleRefuezoTermino(false)
}
After set the modal, I checked what state it's true, and I passed the values in this condition
{modalVisibleEnTermino && <MyModal
message={"Mensaje1"}
state={modalVisibleEnTermino}
/>
}
{modalVisibleFueraTermino && <MyModal
message={"Mensaje2."}
state={modalVisibleFueraTermino}
/>
}
{modalVisibleRefuerzoTermino && <MyModal
message={"Mensaje3."}
state={modalVisibleRefuerzoTermino}
/>
}
Modal
export const MyModal = (props) => {
const [visibleModal, setVisibleModal] = useState(props.state)
return (
<Modal visible={visibleModal}
transparent={true}
animationType='slide'
>
<View style={styles.container}>
<Text style={styles.titulo_label}>¡IMPORTANTE!</Text>
<Text style={styles.info_label}>{props.message}</Text>
<TouchableOpacity style={styles.button_aceptar} onPress={() => setVisibleModal(false)}>
<Text style={styles.text_button}>Aceptar</Text>
</TouchableOpacity>
</View>
</Modal>
);
Add a restore property to your MyModal component
{modalVisibleEnTermino && <MyModal
message={"Mensaje1"}
state={modalVisibleEnTermino}
restore={restoreModals}
/>
}
{modalVisibleFueraTermino && <MyModal
message={"Mensaje2."}
state={modalVisibleFueraTermino}
restore={restoreModals}
/>
}
{modalVisibleRefuerzoTermino && <MyModal
message={"Mensaje3."}
state={modalVisibleRefuerzoTermino}
restore={restoreModals}
/>
}
In the MyModal Component remove the useState and pass the props.state to visible and use the props.restore on the onPress callback like so
export const MyModal = (props) => {
return (
<Modal visible={props.state}
transparent={true}
animationType='slide'
>
<View style={styles.container}>
<Text style={styles.titulo_label}>¡IMPORTANTE!</Text>
<Text style={styles.info_label}>{props.message}</Text>
<TouchableOpacity style={styles.button_aceptar} onPress={() => props.restore()}>
<Text style={styles.text_button}>Aceptar</Text>
</TouchableOpacity>
</View>
</Modal>
);
In your original MyModal Component, by passing the props.state to useState, you are creating a new state within that component. Setting onPress to setVisibleModal(false) will not change the states of modalVisibleEnTermino, modalVisibleFueraTermino, and modalVisibleRefuerzoTermino. By passing restoreModals as a prop to MyModal you can change the states in the Main component from inside MyModal.
One more thing, remove the if statements in your restoreModals function
const restoreModals = () => {
setModalVisibleEnTermino(false)
setModalVisibleFueraTermino(false)
setModalVisibleRefuezoTermino(false)
}
so, in ParentComponent I have
<Component dropDownContent={<DropDownContent content={array} onSelect={handleSelect} />} />
my DropDownContent looks something like this
return (<ul>
{content.map((item) =>
{ return <li><button onClick={()=> onSelect(array.id)}>{array.name}</button></li>}
)}
</ul>)
Can I some how do something with the onSelect inside Component even if I add DropDownContent as a prop to Component?
Thanks :)
What I understand from your question is that you want to pass a function from the parent component to the child component. And when a local function inside child component is clicked, you want to call that passed function. if yes, then this is your solution:
Note: I do not know exactly what code you wrote and what your component consists of. So I will give you the answer by giving a simple example to fully understand the solution.
In your parent component:
export const ParentComponent = props => {
const handleSelect = () => console.log(`do something here`);
return (
<Component
dropDownContent={
<DropDownContent
content={array}
onSelect={() => handleSelect()}
/>}
/>
)
}
And in your child component you need to receive the passed function like below:
export const ChildComponent = props => {
const handlePassedFunction = () => props.onSelect?.();
return (<ul>
{content.map((item) => {
return <li>
<button onClick={() => handlePassedFunction(array.id)}>{array.name}</button>
</li>
}
)}
</ul>)
}
i'm trying to create a list of documents dynamically with semantic-ui-react. I'd like to get the document title back when the list item is clicked. According to the documentation:
https://react.semantic-ui.com/elements/list
there is an onItemClick prop for this reason, however when im using it i get a warning when it's rendered:
Warning: Failed prop type: Prop onItemClick in List conflicts with props: children. They cannot be defined together, choose one or the other.
Also clicking on the list item does nothing (atm i just want to log the doc title to the console). Here is the code:
handleListItemClick(event, data) {
console.log("list item clicked: " + data.value);
}
buildResultsContainer() {
return this.props.listOfResults.map((document,index) =>
{
return (
<List.Item
as='a'
key={index}>
<Icon name='file' />
<List.Content>
<List.Header>{document.properties.title}</List.Header>
<List.Description>
{document.properties.description}
</List.Description>
</List.Content>
</List.Item>
);
}
);
}
render() {
return (
<div>
<List onItemClick={this.handleListItemClick}>
{this.buildResultsContainer()}
</List>
</div>
)
}
Can you please tell me how to use properly the onItemClick prop for the List component?
Less important, do you have any tip how to refactor the list rendering? Just wanted to keep the render function short and clean, but this function call looks a bit overkill....
Thanks a lot!
I think maybe the intent when using onItemClick is that you would use the items prop on List since then you wouldn't have any children e.g.
render() {
const items = this.props.listOfResults.map(document => {
return {
icon: 'file',
content: document.properties.title,
description: document.properties.description,
onClick: e => console.log(document.title)
}
});
return <List items={items} />
}
If you had your listOfResults prop in the above format, you wouldn't even need to do this map and your render function would be super tight:
render() {
return <List items={this.props.listOfResults} />;
}
Alternately, List.Item takes an onClick prop that you could define in your buildResultsContainer() function. Because each onClick function is unique based on the current document object, you will need to use an anonymous function to call your handleClick function as follows:
<List.Item
onClick={() => this.handleClick(document.title)}
...etc
/>
You would then have:
handleClick = docTitle => {
console.log(docTitle);
};
If what you wanted was obtainable from event.target, you could just pass the reference of handleClick to the onClick i.e.
handleClick = event => {
console.log(e.target.innerText);
};
<List.Item
onClick={this.handleClick}
/>
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.
Error Message:
React: Warning setState Cannot update during an existing state
transition (such as within a render)
This is a react-native app, but I figure, this is more of a react question.
I'm getting the error described in the heading. But I'm puzzled about the reason and I'm not setting state in any of the parent or child components. So I have a grandchild component(CardLayoutResult), which renders a listView and each row renders a new component(Render row) which has a click event, upon clicking it calls a function(onselectLayout) from props(passed from the parent component(Personalization component)(parent function setCardTemplate). This parent function then calls setState of the parent internal state. Then re-rendering occurs.
Why am I getting this error upon click?
Parent Component
export class Personalization extends Component {
constructor(props) {
super(props);
this.state = {
showModel: true,
editorState: {}
};
this.setCardTemplate = this.setCardTemplate.bind(this);
}
setCardTemplate(selectedTemplateObj){
console.log(JSON.stringify(selectedTemplateObj));
this.setState({
showModel: false,
editorState: selectedTemplateObj
});
}
render() {
return (
<View>
<CreateCardStep containerState={{showModel:this.state.showModel ,
editorState: this.state.editorState
setCardTemplate:this.setCardTemplate}} />
</View>
)
}
Child Component
const CreateCardStep = (_props) => {
return (
<View>
<CardLayoutResults containerState={_props.containerState} />
</View>
);
}
Grand Child Component
export class CardLayoutResults extends Component {
constructor(props) {
super(props);
}
render() {
return(
<View>
<ListView
RenderRow = {(data) => <RenderRow styles={styles} rowObj={data} onSelectLayout={this.props.containerState.setCardTemplate} /> }
</View>
)
}
Render Row
const RenderRow = (props) => {
let base64Image = 'data:image/png;base64,'.concat(props.rowObj.base);
return (
<View style={props.styles.templateImage}>
<TouchableHighlight onPress={() => props.onSelectLayout(props.rowObj)}>
<Image style={props.styles.thumbnail} resizeMode= {Image.resizeMode.contain} source={{uri: base64Image}}/>
</TouchableHighlight>
</View>
);
};
- Update
This issue seems to be when setting state in the setCardTemplate function
You either need to bind your setCardTemplate function in the constructor or call it from a lambda function (binding is better)
<View>
<ListView renderRow = {(data => rowObj={data}} onSelectLayout={() => this.props.containerState.setCardTemplate} />
</View>
I guess the renderRow for listView is not called properly
Try
<View>
<ListView
renderRow = {(data) => <RenderRow rowObj={data} onSelectLayout={this.props.containerState.setCardTemplate} />}
/>
</View>
and pass down the props from personalization component like
return (
<View>
<CreateCardStep containerState={{showModel:this.state.showModel ,
editorState: this.state.editorState
setCardTemplate:(val) => this.setCardTemplate(val)}} />
</View>
)
The problem is with the way you are passing props to the child component from the parent.
In this snippet
<CreateCardStep containerState={{showModel:this.state.showModel ,
editorState: this.state.editorState
setCardTemplate:(val) => this.setCardTemplate(val)}} />
while passing the function as a prop itself you are calling the function so every time the component render the state will be set which is fine.
But when you click the Render Row component state is being set yet for the same property before finishing the render cycle which causes this warning.
To fix it just pass the function from parent as setCardTemplate: this.setCardTemplate. It will set the state (and internally will call the render) only on row press, not every time it is rendered