How to emit event from JSX functional component in Vuejs - javascript

Im new to the community and VueJs so please have mercy :) If the answer to the question is obvious, and thanks for your effort up-front, I really appreciate it!
I have this component:
<script>
export default {
name: 'InputElement',
functional: true,
render(createElement, context) {
const { validation, name, field } = context.props || {}
const { listeners } = context
// debugger
return (
<input id={name}
v-validate={validation}
type={field.type}
placeholder={field.placeholder}
name={name}
onInput={ listeners.event_is_nice('sadf')}
class="e-form__input"/>
)
}
}
</script>
as you can see it's not standard VueJs syntax, I am trying to return the input element and onInput I am trying to emit "event_is_nice" event.
When I try this, I get:
"listeners.event_is_nice" is not a function (I guess its not registered).
When I use createElement (standard JSX Vue syntax) or I Use
then it works, but I just have no luck figuring this method out..'/

A solution would be:
export default {
name: 'InputElement',
functional: true,
render(createElement, context) {
const { validation, name, field } = context.props || {}
const { listeners } = context
let emitEvent = listeners['event_is_nice'] //ADDED
// debugger
return (
<input id={name}
v-validate={validation}
type={field.type}
placeholder={field.placeholder}
name={name}
onInput={ () => emitEvent("sadf")} // MODIFIED
class="e-form__input"/>
)
}
}
So in your code I added: let emitEvent = listeners['event_is_nice']
and on input handler onInput={ () => emitEvent("sadf")}

So The answer from roli roli is working, I tried in the sendbox,
Which led me to realize that I didn't address the problem well.
This functional component is wrapped in parent "factory" component,
which looks like this:
<script>
import BaseLabel from './elements/BaseLabel'
import BaseInput from './elements/BaseInput'
import BaseMessage from './elements/BaseMessage'
export default {
functional: true,
components: {
BaseInput,
BaseLabel,
BaseMessage
},
props: {
field: {
type: Object,
default: () => {}
},
validation: {
type: String
},
name: {
type: String
},
errorMsg: {
type: String
}
},
render(createElement, { props, listeners }) {
function appropriateElementComponent() {
switch (props.field.type) {
case 'checkbox':
return BaseInput // TODO: Replace with Base Checkbox
default:
return BaseInput
}
}
const label = createElement(BaseLabel, { props })
const input = createElement(appropriateElementComponent(), { props })
const message = createElement(BaseMessage, { props })
// debugger
return [label, input, message]
}
}
</script>
So The parent wrapper component is the one which is not really receiving and passing up the event to the parent component...

Related

How would you use conditional hooks inside a React.Component class

Per documentation, Hooks cannot be used inside class components. But there are ways with higher order components: How can I use React hooks in React classic `class` component?. However this answer provided does not address the case of hooks that get called on function invocation. Take this simple Toast hook from: https://jossmac.github.io/react-toast-notifications/. I'd like to call the hook inside of a class of form:
```
class MyClass extends React.Component {
onTapButton = () => {
if(conditionTrue){
addToast('hello world', {
appearance: 'error',
autoDismiss: true,
})
}
}
render(){ ... }
}
```
There'd be no way of calling addToast without using const { addToast } = useToasts() in the class method, which would throw error.
You can use withToastManager HOC to archive that work
Here is an example
import React, { Component } from 'react';
import { withToastManager } from 'react-toast-notifications';
class ConnectivityListener extends Component {
state = { isOnline: window ? window.navigator.onLine : false };
// NOTE: add/remove event listeners omitted for brevity
onlineCallback = () => {
this.props.toastManager.remove(this.offlineToastId);
this.offlineToastId = null;
};
offlineCallback = id => {
this.offlineToastId = id;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
const { isOnline } = this.state;
if (prevState.isOnline !== isOnline) {
return { isOnline };
}
return null;
}
componentDidUpdate(props, state, snapshot) {
if (!snapshot) return;
const { toastManager } = props;
const { isOnline } = snapshot;
const content = (
<div>
<strong>{isOnline ? 'Online' : "Offline"}</strong>
<div>
{isOnline
? 'Editing is available again'
: 'Changes you make may not be saved'}
</div>
</div>
);
const callback = isOnline
? this.onlineCallback
: this.offlineCallback;
toastManager.add(content, {
appearance: 'info',
autoDismiss: isOnline,
}, callback);
}
render() {
return null;
}
}
export default withToastManager(ConnectivityListener);
For more information you can also find here

React - HOCs Inside Render - Are there Exceptions to the Rule?

I implemented a Pub/Sub pattern by using several component composition techniques: React.cloneElement, and functional "composed components." The goal in mind was to be able to dynamically enroll components into different flows of data by assigning them a "topic" prop.
For example, this component receives all data published to the HELLO_WORLD topic:
<MyComponent topic="HELLO_WORLD" />
Here is the inside of MyComponent expressed as a functional component:
export const MyComponent = props => subscribe(({ topic, data }) => {
return <span>I am listening to the {topic} topic. Current state: {data}</span>
}, props.topic);
Alternatively, here it is expressed as a class component:
class MyComponent extends React.Component {
render() {
const { props: { otherProps, topic } } = this;
return subscribe(({ data }) => {
return <span>I am listening to the {topic} topic. Current state: {data}</span>
}, topic)
}
}
As you can see, this pattern necessitated returning a Higher Order Component inside the render function. Do you think this falls into the caveat mentioned here?
Here's some more context:
The subscribe function returns a composed component:
const subscribe = (Comp, topic) => {
return (
<Subscriber topic={topic}>
<Comp />
</Subscriber>
);
};
Which wraps MyComponent in a Subscriber:
class Subscriber extends Component {
state = publisher.getState(this.props.topic) // get initial state
onMessage = msg => {
this.setState({ ...msg });
return this.state;
}
componentDidMount() {
this.subscription = publisher
.subscribe(this.props.topic, this.onMessage);
}
componentWillUnmount() {
publisher.unsubscribe(this.props.topic, this.onMessage);
}
render() {
const {
state: { data },
props: { children }
} = this;
return Children.map(children, child =>
cloneElement(child, { ...this.props, data })
);
}
}
The subscriber gets its state from the publisher, which caches topics:
const eventEmitter = new EventEmitter();
const publisher = {
subscribe: function (eventName, cache) {
eventEmitter.on(eventName, data => {
this.cache[eventName] = cache(data);
});
},
unsubscribe: function (eventName, fn) {
eventEmitter.off(eventName, fn)
},
send: function (eventName, payload) {
eventEmitter.emit(eventName, payload);
if (!this.cache[eventName]) {
this.cache[eventName] = { data: payload };
}
},
getState: function (topic) {
return this.cache[topic] || {};
},
cache: {}
}
The component profiler suggests that this setup is rendering very efficiently. Additionally, state is persisted in a cache outside of React land. If you ask me, it's pretty much just Flux with a twist. Your thoughts?
Your subscribe() is not a true HOC.
Why ? ( concentrate on bold word )
HOC is a pure Function that returns a container component which
wraps original component.
subscribe() is just wrapper Component which just wraps
original component and return.
Here is a detailed answer :
https://stackoverflow.com/a/64178585/8323442

React Reducer getting called but not changing the state

Ive check if both the action and the reducer are getting called. They both get called but the mapstatetoprops isnt changing the state. Also means that action.tpye is getting change. I dont understand why it wont work and any help is appreciated.
This is my reducer
const calling = (state={}, action) => {
switch (action.type) {
case 'CALLING_USER':
return {type: action.type, callLoading: true, isCalling: true, friend: action.friendSocket}
case 'ANSWERING_CALL':
console.log("ANSWER CALL")
return {type: action.type, callLoading: true, isCalling: true, friend: action.data.socket, offer: action.data.offer}
case 'CALL_SUCCESS':
return {
isCalling: true,
callLoading: false
}
case 'CALL_ERROR':
return {
callLoading: false,
error: true,
callErrorText: action.err,
}
default:
return {isCalling: false}
}
}
export default calling
Action
export const CALLING_USER = 'CALLING_USER'
function callingUser(friendSocket) {
return {
type: CALLING_USER,
friendSocket
}
}
export const ANSWERING_CALL = 'ANSWERING_CALL'
function answeringCall(data) {
return {
type: ANSWERING_CALL,
data
}
}
export const CALL_ERROR = 'CALL_ERROR'
function callError(err) {
return {
type: CALL_ERROR,
err
}
}
export function callSetup(friendSocket, dispatch) {
console.log("IS THIS WORKING")
console.log(dispatch)
return function(dispatch) {
console.log(dispatch)
dispatch(callingUser(friendSocket))
}
}
export function answerSetup(data) {
console.log("ANSWER SETUP")
return function(dispatch) {
dispatch(answeringCall(data))
}
}
It gets called here
import { answerSetup } from './Redux/Actions'
import store from './Redux/store'
import io from "socket.io-client";
export const startGlobalSockets = (mySocket) => {
mySocket.on("gettingCalled", data => {
console.log("GETTING CALLED SOCKET", data)
store.dispatch(answerSetup(data))
})
}
export default startGlobalSockets
It should make this statement true.
const mapStateToProps = (state) => ({
user: state.session.user,
calling: state.calling
})
const isVideo = (this.props.calling.isCalling ?
<Grid style={{width:"100%", height: "100%"}} item sm={10}>
<VideoScreen/>
</Grid> :
<Grid style={{height: '57em', overflow: 'auto'}} item sm={10}>
<div className="parent">
<Post/>
</div>
</Grid>)
...
export default withRouter(connect(mapStateToProps)(Home))
I assume you did not use connect as required. You are importing it but I don’t see where you use it. It would help if you’d post the whole code snippet including the use of connect and mapStateToProps and mapDispatchToProps.
In your example answerSetup returns a function which internally calls answeringCall instead of object with action type and payload (like answeringCall). Unless you have some middlewares like react-thunk which handle functions and you have special logic there, I don't see why not using answeringCall directly in your calling component.

Why is my onChange event not working in React?

I am coding a simple search input component for an app that will eventually become larger, but I am at a loss for why the onChange prop associated with it isn't being called. Here I will show my search input component and the app component into which I import it:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class SearchInput extends Component {
static defaultProps = {
onChange: () => Promise.resolve(),
}
static propTypes = {
onChange: PropTypes.func,
value: PropTypes.string,
}
render() {
const { value } = this.props;
return (
<input className="search-input" type='text' onChange={this.handleChange} value={value}/>
)
}
handeChange = (e) => {
const { onChange } = this.props;
onChange(e);
}
}
And then here's my main app component (very simple still, and keep in mind that I have list-rendering functionality, but that isn't where my issue lies). I'm pretty sure the issue lies somewhere in the handleSearchDidChange method that I wrote up and tacked onto the onChange prop for the SearchInput component:
import React, { Component } from 'react';
import Container from './components/container'
import List from './components/list'
import SearchInput from './components/search-input';
// Styles
import './App.css';
export default class App extends Component {
constructor(props){
super(props)
this.state = {
searchValue: undefined,
isSearching: false,
}
// this.handleSearchDidChange = this.handleSearchDidChange.bind(this);
}
render() {
// in the main render, we render the container component (yet to be styled)
// and then call renderList inside of it. We need "this" because this is
// a class-based component, and we need to tell the component that we are
// using the method associated with this class
return (
<div className="App">
<Container>
{this.renderSearchInput()}
{this.renderList()}
</Container>
</div>
);
}
renderSearchInput = () => {
const { searchValue } = this.state;
return (<SearchInput onChange={this.handleSearchDidChange} value={searchValue}/>)
}
renderList = () => {
// return the list component, passing in the fetchData method call as the data prop
// since this prop type is an array and data is an array-type prop, this is
// acceptable
return <List data={this.fetchData()}/>
}
// possibly something wrong with this method?
handleSearchDidChange = (e) => {
const { target } = e;
const { value } = target;
this.setState({
searchValue: value,
isSearching: true,
});
console.log('value: ', value);
console.log('searchValue: ', this.state.searchValue);
console.log('-------------------------')
}
fetchData = () => {
// initialize a list of items
// still wondering why we cannot put the listItems constant and the
// return statement inside of a self-closing setTimeout function in
// order to simulate an API call
const listItems = [
{title: 'Make a transfer'},
{title: 'Wire money'},
{title: 'Set a travel notice'},
{title: 'Pop money'},
{title: 'Edit travel notice'},
{title: 'test money things'},
{title: 'more test money things'},
{title: 'bananas'},
{title: 'apples to eat'},
{title: 'I like CocaCola'},
{title: 'Christmas cookies'},
{title: 'Santa Claus'},
{title: 'iPhones'},
{title: 'Technology is amazing'},
{title: 'Technology'},
{title: 'React is the best'},
];
// return it
return listItems;
}
You have a typo! Missing the "l" in handleChange :)
handleChange = (e) => {
const { onChange } = this.props;
onChange(e);
}
i run your code in sandBox:
https://codesandbox.io/s/onchange-problem-37c4i
there is no issue with your functionality as far as i can see.
but in this case if onChange dose not work for you is because maybe inside of < SearchInput /> component you don't pass the value up to the parent element.
check the sandBox and notice to the SearchInput1 and SearchInput2

ReactJS/Jest: How to test/mock a function, which is passed to component

I'm trying to do some unit testing using jest/enzyme for my react components.
But I'm facing problems with an function I'm passing to a second component.
I don't understand if I have to test or mock this function. If I have to mock it, I don't know how to do that for a function.
Parent Component
export default class Parent extends Component {
togglePosition (term, event) {
this.setState({
top: term.length >= 3
})
}
render () {
return (
<div>
<Child togglePosition={this.togglePosition} />
</div>
)
}
}
Child component
export default class Child extends Component {
handleChange (event) {
const term = event.target.value
this.props.togglePosition(term) // <-- Test/mock it?
this.setState({
loading: 'loading',
term
})
}
render () {
return (
<div>
<Input id="target-input" onChange={this.handleChange} />
</div>
)
}
}
This is how I do a test for the Child component - testing handleChange:
Unit test (Child)
it('handleChange() should set state.term', () => {
const event = { target: { value: 'test' } }
const wrapper = shallow(<Child />)
wrapper.find('#target-input').simulate('change', event)
const state = wrapper.instance().state
expect(state).toEqual({ loading: 'loading', term: 'test' })
})
Do get this error: TypeError: this.props.togglePosition is not a function
Without actually testing it, I believe this is what you need:
it('handleChange() should set state.term', () => {
const togglePosition = jest.fn();
const event = { target: { value: 'test' } };
const wrapper = shallow(<Child togglePosition={togglePosition} />);
wrapper.find('#target-input').simulate('change', event);
const state = wrapper.instance().state;
expect(state).toEqual({ loading: 'loading', term: 'test' });
expect(togglePosition).toHaveBeenCalledWith('test');
})
Mock the passed function: const togglePosition = jest.fn();, and test the condition/response: expect(togglePosition).toHaveBeenCalledWith('test');.

Categories