I am trying to emit an event from a child component to its parent. I have read the guide at vuejs.org and thought that I had got the syntax correct. My child component (AxisLabel.vue) has
<script setup>
....
const emit = defineEmits(['removeLetter', 'submitLetter'])
const props = defineProps({
stat: Object,
index: Number,
segments: Number
})
const point = computed(() =>
{
try {
return valueToPoint(props.stat.value - 10, props.index, props.segments, true)
}
catch {
return valueToPoint(0, 0, 9, true)
}
})
function click() {
if (props.stat.chosen) {
props.stat.chosen = false;
emit('removeLetter',props.stat.label)
}
else {
props.stat.chosen = true;
emit('submitLetter',props.stat.label)
}
}
....
</script>
<template>
<text #click="click" :class=changeColour() class="prevent-select"
:x="point.x" :y="point.y">{{stat.label}}
</text>
</template>
and in the parent (app.vue) I have:
<script setup>
import AxisLabel from './components/AxisLabel.vue'
const tryingWord = ref('word')
function addToWord(letter) {
tryingWord.value = tryingWord.value + letter
}
function removeFromWord(letter) {
// ....todo....
}
</script>
<template>
<svg width="300" height="300">
<PolyGraph :stats="stats"></PolyGraph>
</svg>
<text class="prevent-select" rows="1" cols="9">
{{tryingWord}}
</text>
<AxisLabel #submit-letter = "addToWord"/>
<AxisLabel #remove-letter = "removeFromWord"/>
</template>
When I run this I get
[Vue warn]: Unhandled error during execution of render function
at <AxisLabel onSubmitLetter=fn > at
followed by an 'undefined' error (see below). This happens as soon as the app is started, i.e. without the click event being fired.
If I remove the event listener definitions from app.vue, the error doesn't occur.
[later]
I have now seen that, without the event listener defined, the <text> element of AxisLabel is called exacly 9 times (as it should be) - it's called from another component:
<axis-label
v-for="(stat, index) in stats"
:stat="stat"
:index="index"
:segments="stats.length-1">
and 'stats' has 9 members. But when I add back the event listeners, this element attempts to get created a tenth time, with invalid parameters, causing the crash - the Chrome debugger indicates that 'props' is undefined in here :
<text #click="click" :class=changeColour() class="prevent-select"
:x="point.x" :y="point.y">{{stat.label}}
</text>
'props' is used to calculate the position of the text, but adding the try/catch where it is used hasn't helped.
I need to see a working example of emitting an event in vue - vuejs.org doesn't seem to provide one.
There is an error inside your event handler function. I'm not sure, if this is the problem, but it might be.
const tryingWord = ref('word')
function addToWord(letter) {
// when working with refs, you need to use the .value property
tryingWord.value = tryingWord.value + letter
}
Also, you should never mutate data, that is passed from the props as you do here:
function click() {
if (props.stat.chosen) {
// this mutates the props
props.stat.chosen = false;
emit('removeLetter',props.stat.label)
}
else {
// this mutates the props
props.stat.chosen = true;
emit('submitLetter',props.stat.label)
}
}
you will find an explanation about the one way data flow in the vue documentation.
Update
As mentioned in the comments, the AxisLabel component does not receive any props which will lead to errors when accessing the props inside the <script setup>.
// the following line will throw an error
// because props.stat === undefined
if (props.stat.chosen) {
// ...
}
You could either define a default property that is used when no property is passed or check the prop for undefined before using it.
The problem was here:
<template>
<svg width="300" height="300">
<PolyGraph :stats="stats"></PolyGraph>
</svg>
<text class="prevent-select" rows="1" cols="9">
{{tryingWord}}
</text>
<AxisLabel #submit-letter = "addToWord"/>
<AxisLabel #remove-letter = "removeFromWord"/>
</template>
In fact there are 2 mistakes: Firstly, there are 2 calls to Axislabel and there should be just one:
<AxisLabel #submit-letter = "addToWord" #remove-letter = "removeFromWord"/>
Secondly, since Axislabel is a child of PolyGraph, I needed to propogate the events up through there to reach App.vue, i.e. these event handlers should be defined in PolyGraph.vue, not in App.vue
Related
I am trying to use useRef hook this way.
I want to get the value of textInput with it
let tagsRef = useRef('');
<TextInput
ref={tagsRef}
style={styles.textInputStyle}
placeholder={'Add tags to subscribe to them'}
placeholderTextColor={'lightgray'}
/>
</View>
I am using react-native version:0.63.3
When I
console.log(----tag', tagsRef.current.focus());
It gives me undefined
Any suggestions please?
Check this its Officially RN Documentation
https://reactjs.org/docs/hooks-reference.html#useref
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
It is depending on where do you use the ref. The ref is only filled after the first rendering. You can use it for instance in an event handler on the element.
Another point: You might see that the focus method does not return anything. So when you see an undefined in your console, it is properly related to that problem.
But without further context of your code and your problem, it is hard to give a more detailed answer.
I'm experiencing a strange issue where the onPress function of a functional child component is triggered whenever the component is rendered.
Relevant parent code which renders the child component from a list of dates. I was also having issues with array.map if the component was a class component, but that's a separate issue.
return list_items.map(data =>
<ChildComponent
key={data.day}
props={data}
/>);
And the child component:
const ChildComponent = ({ props }) => {
let date = props.date;
const someFunction = () => {
console.log(date)
}
return (
<Text><Icon name="angle-right" onPress={someFunction()}/></Text>
)
And what I get from the console upon rendering the relevant scene:
2018-11-11
2018-11-12
2018-11-13
2018-11-14
2018-11-15
2018-11-16
Pressing the icon does nothing, yet it seems to run the function as if it was a lifecycle component. I've also moved the function into the parent component and passed it to the child as recommended per Guruparan Giritharan, and it still executes on render.
return list_items.map(data =>
<ChildComponent
key={data.day}
props={data}
someFunction={this.someFunction} // function now within parent component
/>);
And the updated child:
const ChildComponent = ({ props, someFunction }) => {
return (
<Text>
<Icon name="angle-right" onPress={someFunction(props.date)}/>
</Text>
)
}
Change to
<Text><Icon name="angle-right" onPress={someFunction}/></Text>
You are calling the function there itself instead of passing the reference. So the function is called inside each render instead of onPress.
I try to open an edit screen for relevant record when user taps the row in the list. I see at debugger all of the props are passing successfully but somehow I cant show them on the screen. I searched a lot and I think the main problem is at onRowPress helper. When I press the row, I see in the debugger all the props have passed correctly. But there is an error that says
Failed prop type: Invalid prop value of type array supplied to
TextInput, expected string.
My question is how should I handle this error.
console.log
onRowPress() {
console.log(this.props.employee);
Actions.employeeEdit({ employee: this.props.employee });
}
I guess you are on "The Complete React Native and Redux Course", if so, i thing you missed:
-1 import Communications from "react-native-communications";
-2
class EmployeeEdit extends Component {
state = { showModal: false };
...
3- Above your render():
onTextPress() {
const { phone, shift } = this.props;
Communications.text(phone, `Your upcoming shift is on ${shift}`);
}
onAccept() {
const { uid } = this.props.employee;
this.props.employeeDelete({ uid });
}
onDecline() {
this.setState({ showModal: false });
}
-4 And finally, your main render() should be:
<Card>
<EmployeeForm />
<CardSection>
<Button onPress={this.onButtonPress.bind(this)}>Save Changes</Button>
</CardSection>
<CardSection>
<Button onPress={this.onTextPress.bind(this)}>Text Schedule</Button>
</CardSection>
<CardSection>
<Button onPress={() => this.setState({ showModal: !this.state.showModal })}>
Fire Employee
</Button>
</CardSection>
<Confirm
visible={this.state.showModal}
onAccept={this.onAccept.bind(this)}
onDecline={this.onDecline.bind(this)}
>
Are you sure you want to delete this?
</Confirm>
</Card>;
It takes time but I solved the problem. Main problem was the error below;
Failed prop type: Invalid prop value of type array supplied to
TextInput, expected string.
I followed all the code step to step and find that the reducer which update the props with the values I pass can't do this. I added a toString() method to the actions.payload.value and everything is ok. You have to pass a string to the Input component.
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case EMPLOYEE_UPDATE:
return { ...state, [action.payload.prop]: action.payload.value.toString() };
case EMPLOYEE_CREATE:
return INITIAL_STATE;
case EMPLOYEE_SAVE_SUCCESS:
return INITIAL_STATE;
default:
return state;
}
};
When I define a button in React Native as:
<Button ref="buttonRef" title="Button" onPress={this.buttonPressed}/>
And its onPress function as:
buttonPressed(){
this.refs.buttonRef.setNativeProps({color:"#ffffff"});
}
I get the following error:
this.refs.buttonRef.setNativeProps is not a function. (In
'this.refs.buttonRef.setNativeProps({
color: "#ffffff"
})', 'this.refs.buttonRef.setNativeProps' is undefined)
However, if I were to define any other type of component, e.g. text input as
<TextInput ref="userInputRef" value={"this is text"} />
With the function changing its props:
buttonPressed(){
this.refs.textRef.setNativeProps({color:"#ffffff"});
}
Everything changes correctly.
Is there a reason that the button component is unable to have its native props set through setNativeProps?
Button component is a simple custom component created from Touchable components and it doesn't have a ref property. You can check the source code of Button component here.
If you need to change properties of a component you should use state values for that.
Sample
buttonPressed = () => {
this.setState({color:"#ffffff"});
}
//....
<Button ref="buttonRef" color={this.state.color} title="Button" onPress={this.buttonPressed}/>
Have added a tooltip and trigger to my component, how can i get it to fire the tooltip?
required modules like this:
var Icon = require('./Icon'),
Tooltip = require('./Tooltip'),
Button = require('react-bootstrap').Button,
OverlayTrigger = require('react-bootstrap').OverlayTrigger;
getTooltip: function() {
return <Tooltip text="sample text" />;
},
Rendering trigger like this:
<OverlayTrigger placement="right" overlay={this.getTooltip()}>
<Button bsStyle="default">Button text to trigger</Button>
</OverlayTrigger>
But nothing happens?
How can I get this to fire?
Thanks
Tooltip component is:
var TooltipBS = require('react-bootstrap').Tooltip;
var Tooltip = React.createClass({
render: function() {
return (
<TooltipBS>
{this.props.text}
</TooltipBS>
)
}
});
module.exports = Tooltip;
I assume you are using react-bootstrap tooltip?
If you read the documentation here http://react-bootstrap.github.io/components.html#tooltips-in-text you can see how to use it in your code.
<OverlayTrigger placement="right" overlay={this.getTooltip()} trigger="click">
<Button bsStyle="default">Button text to trigger</Button>
</OverlayTrigger>
You can add preferred trigger event with trigger property.
It should be:
getTooltip: function() {
return <Tooltip id="tooltip">sample text</Tooltip>;
},
I believe you will need a render() within the component class. Then you will need a ReactDOM.render(<Tooltip />, document.getElementById('app'); outside of the class.