Trigger onProperty of component - reactjs

I have tried to trigger (refer, call, correct terminology?) an onScale from the property of this component from the react-native-photo-view API:
<PhotoView
source={{ uri: }}
onLoad={() => console.log('onLoad called')}
onTap={(event) =>
console.log(`onTap called: ${event.nativeEvent.x}${event.nativeEvent.y}`)}
onScale={() => console.log('onScale called')}
minimumZoomScale={1}
maximumZoomScale={3}
scale={0.5}
resizeMode={'contain'}
androidScaleType={'fitXY'}
style={styles.photo}
/>
How could i "trigger" this onScale={} property from lets say a <Button onClick={triggerOnScale()} /> to zoom with the scale factor? Any direct code example or pointers to techniques/methods that could help would be greatly appreciated!

If onScale is a method of your PhotoView component then you can set up a reference to call it:
handleOnClick = () => this.photoView.onScale()
//just use the handleOnClick wherever you want your click and it will call onScale also
<PhotoView
source={{ uri: }}
onLoad={() => console.log('onLoad called')}
onTap={(event) =>
console.log(`onTap called: ${event.nativeEvent.x}${event.nativeEvent.y}`)}
**ref={(photoView) => this.photoView = photoView}**
minimumZoomScale={1}
maximumZoomScale={3}
scale={0.5}
resizeMode={'contain'}
androidScaleType={'fitXY'}
style={styles.photo}
/>

If you want your onScale handler and your button's onClick handler to fire the same function, then you can just bind a function to the containing component and then pass the function's reference to the Button and PhotoView respectively.
class YourComponent extends Component {
constructor(props){
super(props);
this.someFunction = this.someFunction.bind(this);
}
someFunction() {
console.log('Inside some Function.);
}
render() {
return(
<div>
<PhotoView onScale={this.someFunction} />
<Button onClick={this.someFunction} />
</div>
);
}
}

Related

React render prop - why is the function not called?

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

Pass parameter to component 2 levels deep React Native

Im trying to pass parameters through to a component two levels deep.
I have 2 screens (MainScreen & UserProfileScreen) with a flat list on both screens, both flat lists use the same component EventCard in its renderItem. EventCard is made up of 3 three nested components EventCardHeader,EventCardBody & EventCardFooter. How do I pass certain arguements only from the UserProfileScreen? I have posted code below to give a better understanding of what I have.
MainScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
/>
openEditEvent = (event) => {
this.props.navigation.navigate('EventFormScreen', {
event: event,
eventKey: this.state.eventKey,
editMode: true,
});
};
EventCard
export default class EventCard extends Component {
render() {
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent={() => this.openEditEvent()}
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}
};
EvenCardHeader
export default class EventCardHeader extends Component {
render() {
return (
<CardSection style={styles.eventCardHeader}>
<Thumbnail small
style={styles.eventOrganiserImage}
source={{uri: this.props.eventOrganiserImage}}/>
<Text style={styles.eventPromoterName}>{this.props.eventVenue}</Text>
{!!this.props.openEditEvent &&
<Button onPress={() => this.props.openEditEvent()}>
Edit
</Button>
}
{!!this.props.openDeleteEventAlert &&
<Button onPress={() => this.props.openDeleteEventAlert()}>
Delete
</Button>
}
</CardSection>
);
}
}
I can see that because I have hardcoded the this.openEditEvent() function into my EventCard component that what's causing me the problem, because then the if statement in EventCardHeader that checks if the this.openEditEvent() exists always evaluates to true. Would someone be able to help show me the right way to do this? Thanks in advance for any help.
UPDATE:
Added in openEditEvent
Where is openEditEvent() declared? It should be in the parent component and passed as props to whatever children you need it in. You can continue to pass it as props from children to children.
EDIT:
Ok so you can pass openEditEvent as props like so:
<EventCard
openEditEvent = this.openEditEvent
openEventDetail={() => this.openEventDetail(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
That function can be available in EventCard, and can then be passed AGAIN as props to another child component:
render() {
var openEditEvent = this.props.openEditEvent;
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent = openEditEvent
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}

Strange behavior of parametrised function passed as a prop in React

I am probably not seeing some obvious thing right here, but i feel pretty stuck with this one.
I have a function
public handleTest = (testNum: number) => {
console.log(testNum);
};
And the following case: I have a component to which I want to pass this function to further use it there onCLick event.
<Controls handleTest={() => this.handleTest}>
<Button label="Test1" clicked={() => this.handleTest(42)} />
</Controls>
The child component is the following:
interface IProps {
handleTest: (type: number) => void;
}
class Controls extends React.Component<IProps, {}> {
public render() {
const {
handleTest
} = this.props;
return (
<React.Fragment>
{this.props.children}
<button onClick={handleTest(42)} label="Test2" />
</React.Fragment>
);
}
}
The interesting thing about this case is that in case of Test2 button, it seems not to recognize the argument passed to it while it logs an object -
Object { dispatchConfig: {…}, _targetInst: {…}, nativeEvent: click, type: "click", target: button.sc-bwzfXH.gonlMM, currentTarget: button.sc-bwzfXH.gonlMM, eventPhase: 3, bubbles: true, cancelable: true, timeStamp: 689, … }
In case of Test1 everything works correctly. I am wondering what am I doing wrong and whether it is typescript that messes things up or some mistake of mine
This is because when you are initilizing the <Controls /> component, the function which you are sending is something like this:
() => this.handleTest
So in the button Test2 you are executing nothing because last function just return a function
So if you want to solve this:
<Controls handleTest={(number) => this.handleTest(number)}>
<Button label="Test1" clicked={() => this.handleTest(42)} />
</Controls>
Also if you do it in this way when <Controls /> is being rendered, your function is gonna be executed and not when the user click on it. To solve this you need to change it in this way:
<React.Fragment>
{this.props.children}
<button onClick={()=>handleTest(42)} label="Test2" />
</React.Fragment>
To optimize just send the function. This is function injection
<Controls handleTest={this.handleTest}>
<Button label="Test1" clicked={() => this.handleTest(42)} />
</Controls>
You're passing a function call to the handler, this would only work if you had curried the original function instead.
<button onClick={handleTest(42)} label="Test2" /> instead of
<button onClick={() => handleTest(42)} label="Test2" />
<button onClick={handleTest(42)} label="Test2" /> would work if handleTest looked like this:
public handleTest = (testNum: number) => () => {
console.log(testNum);
};
EDIT: I would actually recommend utilizing handlers the second way, when you need to infuse an outside parameter into the handler function. By declaring a handler through
{() => doSomething()}, every time you render the component that handler function will be initialized again.
Regardless, that's just a small optimization in most cases.

Use child component function to pass data to redux store via parent component/container

I'm new to React & Redux so it is difficult for me to explain in plain English but I'll try my best. I have a parent/container component that forms the main 'page' of my app. In this component I am rendering a header and various fields like so:
What I want to achieve is for any user input in the title field to be reflected where it currently says 'Untitled Practice' in the header.
The parent component looks like this (excluding various imports for brevity):
export class DrillCreator extends Component {
render() {
return (
<div>
<EditHeader />
<div className="container-fluid max-width-container">
<InputWithTooltip
type={'text'}
placeholderText={'Title'}
tooltipText={'Title Tooltip'}
required
/>
<InputWithTooltip
type={'textarea'}
placeholderText={'Summary'}
tooltipText={'Summary Tooltip'}
/>
<InputWithTooltip
type={'file'}
placeholderText={'Hero Image/Video'}
tooltipText={'Hero Image/Video Tooltip'}
/>
<InputWithTooltip
type={'select'}
options={['7', '8', '9', '10']}
placeholderText={'Ages'}
tooltipText={'Ages Tooltip'}
required
/>
</div>
</div>
);
}
}
The <InputWithTooltip /> component is essentially a container that renders the appropriate input along with a tooltip component:
export default class InputWithTooltip extends Component {
constructor(props) {
super(props);
this.state = {
textEntered: '',
};
}
render() {
let input = null;
if (this.props.type === 'text') {
input = (
<TextInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'select') {
input = (
<SelectInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'textarea') {
input = (
<TextAreaInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'file') {
input = (
<FileInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
}
return (
<div>
<InputTooltip tooltipText={this.props.tooltipText} />
{input}
</div>
);
}
}
As you can see, I have a textEntered state which is updated via an onChange function passed via the updateText props.
I have set up Redux so that I am able to call a dispatch function to set the title field in my reducer. This works fine if I simplify my parent component and simply call the <TextInput /> component which has the updateText prop:
export class DrillCreator extends Component {
constructor() {
super();
this.state = {
textEntered: '',
};
}
render() {
return (
<TextInput
placeholderText="Title"
updateText={(textEntered) => {
this.setState({ textEntered });
this.props.setDrillTitleAction({ textEntered });
}}
/>
);
}
}
const mapDispatchToProps = dispatch => ({
setDrillTitleAction: drillCreator => dispatch(setDrillTitle(drillCreator)),
});
export default connect(null, mapDispatchToProps)(DrillCreator);
The issue I have is that I want to call setDrillTitleAction from <InputWithTooltip /> instead of <TextInput /> as this is the only field in the form that I want to do anything special with.
Like I said I'm new to React and Redux so could be massively overcomplicating something or completely missing the point so any pointers would be massively helpful. Thanks.
You can pass the dispatch function from the container component in as a prop to the display component, and then call it when the value is changed.
In your case you need to pass setTitleDrillAction in as a prop to InputWithTooltip and then call if from within your updateText callbacks.
One thing to point out is that you will be storing the text box value multiple times - on the redux state and the InputWithTooltip state. You might choose to just make InputWithTooltip a stateless component which receives its value as a prop and dispatches updates to redux (via its parent container as described above).

React: Warning setState Cannot update during an existing state transition (such as within a render)

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

Resources