Undefined function when importing it from different file? - reactjs

So, I have this component
<ConfirmApplicationPopUp
onConfirm={() => handleConfirmApplication(true)}
/>
whenever i try to run the function[it's passed to the props of ConfirmApplicationPopUp component as you can see] it tells me that it's undefined
my function is in another file
I import it like this
import handleConfirmApplication from '../shift-details/utils';
the function itself is
export const handleConfirmApplication = async (isInvite) => {
const checkVals =
get(`shift${isInvite ? 'Invite' : ''}.account.accountName`, this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
if(isInvite) {
await this.handleShiftInviteDecision('ACCEPT')();
}
else {
const shiftId = this.props.shift.id;
const {
data: { updatedShifts },
} = await this.props.updateMyApplication(shiftId, 'APPLY');
this.setState({
updatedShift: updatedShifts.find(({ id }) => id === shiftId),
});
}
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
}
Any ideeas why that happens? I have checked some of the other topics about this but with no avail :(

It's because you are not exporting the function as default.
If you import it in the following way it should work.
import { handleConfirmApplication } from '../shift-details/utils';

Related

converted HTML to Editorstate but not be able to use

i writen a function to convert HTML to editorState. it's work as i want but i can't use it when i assign it into the draft-wysiwyg
first function i use to call to change HTML to EditorState this function will call the other function and create EditorState on it own
function convertToEditor(markup: string): EditorState {
if (markup === '<p></p>') {
return EditorState.createEmpty()
}
const root = markupToObject(markup)
const blockContentArray: Array<ContentBlock> = []
_.forEach(root, (node, i) => {
const contentBlock = new ContentBlock({
key: genKey(),
text: node.textContent,
type: 'unstyled',
characterList: Immutable.List(getCharactorList(node.childNodes)),
data: node.getAttribute('style')?.includes('text-align') ? { 'text-align': node.getAttribute('style').split(':')[1].replace(';', '') } : {},
})
blockContentArray.push(contentBlock)
})
const contentState: ContentState = ContentState.createFromBlockArray(blockContentArray)
const editorState: EditorState = EditorState.createWithContent(contentState)
console.log(editorState)
return editorState
}
markupToObject and getCharactorList is just a function that i write to lower the complexity of the coding
function markupToObject(markup): HTMLCollection {
const div = document.createElement('div')
div.innerHTML = markup.trim()
return div.children
}
function getCharactorList(nodes: NodeListOf<ChildNode>, style?: Array<'BOLD' | 'ITALIC' | 'UNDERLINE'>): Array<CharacterMetadata> {
const characterList: Array<CharacterMetadata> = []
_.forEach(nodes, (node) => {
if (node.nodeName === '#text') {
_.forEach(node.textContent, () => {
characterList.push(CharacterMetadata.create({ style: style, entity: null }))
})
} else if (node.nodeName === 'A') {
_.forEach(node.textContent, () => {
characterList.push(CharacterMetadata.create({ style: [...(style || []), 'BOLD'], entity: null })) /* entity ID? */
})
} else {
const newStyle: Array<'BOLD' | 'ITALIC' | 'UNDERLINE'> = []
if (node.nodeName === 'STRONG') {
newStyle.push('BOLD')
} else if (node.nodeName === 'EM') {
newStyle.push('ITALIC')
} else if (node.nodeName === 'INS') {
newStyle.push('UNDERLINE')
}
characterList.push(...(getCharactorList(node.childNodes, [...newStyle, ...(style || [])]) || []))
}
})
return characterList
}
how i use
<RichText
onChange={(e) => {}}
value={convertToEditor(
'<p>text <strong>bold</strong> <strong><em>bold+italic</em></strong> </p> <p style="text-align:center;">center</p> <p><strong> link </strong></p> <p><strong> #name surname </strong></p>'
)}
/>
this is the error i got after use the Editorstate that convert from HTML
Uncaught TypeError: Cannot read properties of undefined (reading 'toList')

Testing and mocking a call with jest not working

I'm trying to test the following function:
// playlist.js
export function getSimplePlaylist() {
// something here
}
export function getPlaylist(type, settings) {
let options = { type };
if (type === 'simple') {
options.getData = () => {
const { videos } = settings;
return getSimplePlaylist(videos);
};
}
// There are few more cases here, but no need to post them
return options;
}
I have tried a bunch of different ways of testing that, but no luck, i.e:
//playlist.spec.js
import * as playlist from '.';
describe('getPlaylist', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('should get correct option when static ', () => {
playlist.getSimplePlaylist = jest.fn();
const videos = playlist.getPlaylist('simple', { videos: [1, 2, 3] });
videos.getData()
expect(playlist.getSimplePlaylist).toBeCalled();
});
});
Any ideas on how I can test something like the above? Thanks!
If you use the function that way you use the JS functional programming. getPlayList will always call original getSimplePlaylist. If you want to do it you should use class:
class Playlist {
def() {
return 'def';
}
abc(cond) {
if (cond) {
return this.def();
}
}
}
export default Playlist;
and then you could test it:
import Playlist from './playlist';
describe('Name of the group', () => {
it('should ', () => {
const play = new Playlist();
play.def = jest.fn().mockReturnValue('mock');
expect(play.abc(true)).toEqual('mock');
expect(play.def).toBeCalled();
});
});
or you could use function with default implementation and then in test pass additional parameter:
// playlist.js
export function getSimplePlaylist() {
// something here
}
export function getPlaylist(type, settings, simplePlaylistFunc=getSimplePlaylist) {
let options = { type };
if (type === 'simple') {
options.getData = () => {
const { videos } = settings;
return simplePlaylistFunc(videos);
};
}
// There are few more cases here, but no need to post them
return options;
}
Probably the easiest solution here to mock function in a file would be to not export them individually but from an object and use it from that object in the particular module
// playlist.js
function getSimplePlaylist() {
// something here
}
function getPlaylist(type, settings) {
let options = { type };
if (type === 'simple') {
options.getData = () => {
const { videos } = settings;
return funcs.getSimplePlaylist(videos);
};
}
// There are few more cases here, but no need to post them
return options;
}
const funcs = {
getSimplePlaylist,
getPlaylist
}
export default funcs;
Now you can test them like
//playlist.spec.js
import playlist from '.';
describe('getPlaylist', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('should get correct option when static ', () => {
playlist.getSimplePlaylist = jest.fn();
const videos = playlist.getPlaylist('simple', { videos: [1, 2, 3] });
videos.getData()
expect(playlist.getSimplePlaylist).toBeCalled();
});
});

React Native function not triggering

So, I have this component,
{ this.props.isConfirmModalOpen && shiftInvite && <ConfirmApplicationPopUp
memberPhoto={props.memberProfile && props.memberProfile.photo}
venueLogo={getOr('noop', 'account.logo', shiftInvite)}
isOpen={props.isConfirmModalOpen}
shift={shiftInvite}
isOnboarding={isOnboardingMember}
onClose={props.onToggleConfirmPopUp}
onConfirm={this.handleConfirmApplication}
checkValues={props.confirmationCheckValues}
onUpdateCheckValues={props.onUpdateConfirmationCheckValues}
isHomeComponent
/> }
As you can see, I pass on onConfirm the handleConfirmApplication function, which is a check for some stuff and has to run a function in the try block, , here's the function
handleConfirmApplication = async () => {
const checkVals =
get('shift.account.accountName', this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
console.log('inTry1');
await this.handleShiftInviteDecision('ACCEPT');
console.log('inTry2');
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
console.log('inFinally');
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
My problem is, for whatever reason, it doesn't run the handleShiftInviteDecision('ACCEPT) for whatever reason, i'm awaiting it, tried to put it in another function, call them both from another function ETC, the function does not run!
Here's the handleShiftInviteDecision function too
handleShiftInviteDecision = (decision: 'ACCEPT' | 'DECLINE') => async () => {
console.log('handleSIDecision1');
const [shiftInvite] = getOr([], 'shiftInvites', this.state.modals);
console.log('handleSIDecision2');
if (decision === 'ACCEPT') {
analytics.hit(new PageHit(`ShiftInviteModal-ACCEPT-${shiftInvite.id}`));
console.log('handleSIDecision3');
} else if (decision === 'DECLINE') {
analytics.hit(new PageHit(`ShiftInviteModal-DECLINE-${shiftInvite.id}`));
console.log('handleSIDecision4');
}
try {
console.log("thisSHouldRun")
this.setState({ isLoading: true, display: false });
await this.props.updateMyApplication(shiftInvite.id, decision);
console.log('handleSIDecision5');
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.setState({ isLoading: false, display: false });
}
};
Any ideeas on what I could do?
The function handleShiftInviteDecision is a function that returns an async function.
handleShiftInviteDecision = (decision: 'ACCEPT' | 'DECLINE') => async () => {
So, you would need to call the async function it returns to invoke it:
try {
console.log('inTry1');
await this.handleShiftInviteDecision('ACCEPT')(); // <--- here
console.log('inTry2');
} catch (e) {

How can I put this function into a component?

I have a react native codebase in which I import a component and use the same function twice but with slight differences. I would like to outsource it into a new component somehow. Any ideas?
It looks like this :
handleConfirmApplication = async () => {
const checkVals =
get('shiftInvite.account.accountName', this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
await this.handleShiftInviteDecision('ACCEPT')();
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
And the second one is the following :
handleConfirmApplication = async () => {
const checkVals =
get('shift.account.accountName', this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
const shiftId = this.props.shift.id;
const {
data: { updatedShifts },
} = await this.props.updateMyApplication(shiftId, 'APPLY');
this.setState({
updatedShift: updatedShifts.find(({ id }) => id === shiftId),
});
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
Simply use an if/else statement in your try/catch and a ternary condition to create your string. Choosing between one or another should be done by passing a parameter to your function :
handleConfirmApplication = async (isInvite) => {
const checkVals =
get(`shift${isInvite ? 'Invite' : ''}.account.accountName`, this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
if(isInvite){
await this.handleShiftInviteDecision('ACCEPT')();
}
else{
const shiftId = this.props.shift.id;
const {
data: { updatedShifts },
} = await this.props.updateMyApplication(shiftId, 'APPLY');
this.setState({
updatedShift: updatedShifts.find(({ id }) => id === shiftId),
});
}
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
And calling it :
handleConfirmApplication(true)
Have I missed any other differences between your functions ?
To Use it in a reusable component :
handleConfirmApplication = async () => {
const { isInvite } = this.props
const checkVals =
And calling it :
<MyComponent isInvite={false} /> //Just switch it to true to get the other version

Redux mutation detected between dispatches

I'm having some trouble with a react redux I'm currently working on. I'm relatively new to Redux so maybe I'm missing a simple concept here but what I'm trying to do is build a deck building app for a card game and I want to be able to save the deck anytime a user adds or removes a card from their deck.
However, anytime I click add or remove I'm receiving the following error message while trying to dispatch an update action.
The error message reads as follows:
Uncaught Error: A state mutation was detected between dispatches, in the path `decks.0.cards.mainboard.0.quantity`. This may cause incorrect behavior.
My container component
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import DeckMobileDisplay from './DeckMobileDisplay';
import * as deckActions from '../../actions/deckActions';
export class DeckEditorContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
deck: Object.assign({}, this.props.deck)
}
this.addCard = this.addCard.bind(this);
this.removeCard = this.removeCard.bind(this);
}
addCard(board, cardName) {
let deck = this.state.deck;
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName)
i.quantity += 1;
});
const update = Object.assign(deck, cards);
this.props.deckActions.updateDeck(update).then(deck => {
console.log(deck);
})
.catch(err => {
console.log(err);
});
}
removeCard(board, cardName) {
let deck = this.state.deck;
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
i.quantity -= 1;
}
}
});
const update = Object.assign(deck, cards);
this.props.deckActions.updateDeck(update).then(deck => {
console.log(deck);
})
.catch(err => {
console.log(err);
});
}
render() {
const deck = Object.assign({}, this.props.deck);
return (
<div className="editor-container">
<DeckMobileDisplay
deck={deck}
addCard={this.addCard}
removeCard={this.removeCard}
/>
</div>
);
}
}
DeckEditorContainer.PropTypes = {
deck: PropTypes.object
};
function getDeckById(decks, id) {
const deck = decks.filter(deck => deck.id == id);
if (deck.length) return deck[0];
return null;
}
function mapStateToProps(state, ownProps) {
const deckId = ownProps.params.id;
let deck = {
id: '',
userId: '',
cards: []
}
if (state.decks.length > 0) {
deck = getDeckById(state.decks, deckId);
}
return {
deck: deck
};
}
function mapDispatchToProps(dispatch) {
return {
deckActions: bindActionCreators(deckActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DeckEditorContainer);
Component for DeckMobileDisplay
import React, {PropTypes} from 'react';
import TabContainer from '../common/Tabs/TabContainer';
import Tab from '../common/Tabs/Tab';
import CardSearchContainer from '../CardSearch/CardSearchContainer';
import DeckList from './DeckList.js';
class DeckMobileDisplay extends React.Component {
render() {
return (
<TabContainer>
<Tab title="DeckList">
<DeckList
deck={this.props.deck}
addCard={this.props.addCard}
removeCard={this.props.removeCard}
/>
</Tab>
<Tab title="Search">
<CardSearchContainer
addCard={this.props.addCard}
removeCard={this.props.removeCard}
/>
</Tab>
<Tab title="Stats">
<p>stats coming soon...</p>
</Tab>
</TabContainer>
);
}
}
DeckMobileDisplay.propTypes = {
deck: PropTypes.object.isRequired,
addCard: PropTypes.func.isRequired,
removeCard: PropTypes.func.isRequired
}
export default DeckMobileDisplay;
Related Actions
export function createDeck(deck) {
return dispatch => {
dispatch(beginAjaxCall());
const config = {
method: 'POST',
headers: { 'Content-Type' : 'application/json' },
body : JSON.stringify({deck: deck})
};
return fetch(`http://localhost:3000/users/${deck.userId}/decks`, config)
.then(res => res.json().then(deck => ({deck, res})))
.then(({res, deck}) => {
if (res.status >= 200 && res.status < 300) {
dispatch(createDeckSuccess(deck.deck));
}
else
dispatch(createDeckFailure(deck));
})
.catch(err => {
console.log(err);
dispatch(ajaxCallError(err));
});
};
}
export function updateDeck(deck) {
return dispatch => {
dispatch(beginAjaxCall());
const body = JSON.stringify({deck: deck});
const config = {
method: 'PUT',
headers : { 'Content-Type' : 'application/json' },
body: body
};
return fetch(`http://localhost:3000/decks/${deck.id}`, config)
.then(res => res.json().then(deck => ({deck, res})))
.then(({res, deck}) => {
if (res.status >= 200 && res.status < 300) {
dispatch(updateDeckSuccess(deck.deck));
}
dispatch(ajaxCallError(err));
})
.catch(err => {
console.log(err);
dispatch(ajaxCallError(err));
});
};
}
export function updateDeckSuccess(deck) {
return {
type: types.UPDATE_DECK_SUCCESS,
deck
};
}
And my deck Reducer
import * as types from '../actions/actionTypes';
import initialState from './initialState';
export default function deckReducer(state = initialState.decks, action) {
switch (action.type) {
case types.LOAD_USERS_DECKS_SUCCESS:
return action.decks;
case types.CREATE_DECK_SUCCESS:
return [
...state,
Object.assign({}, action.deck)
]
case types.UPDATE_DECK_SUCCESS:
return [
...state.filter(deck => deck.id !== action.deck.id),
Object.assign({}, action.deck)
]
default:
return state;
}
}
If you need to see more of the app the repo is here:
https://github.com/dgravelle/magic-redux
Any kind of help would be appreciated, thanks!
Your problem is caused because you are modifying component's state manually.
One Redux's principle is:
State is read-only
The only way to change the state is to emit an action, an object
describing what happened.
This ensures that neither the views nor the network callbacks will
ever write directly to the state. Instead, they express an intent to
transform the state. Because all changes are centralized and happen
one by one in a strict order, there are no subtle race conditions to
watch out for. As actions are just plain objects, they can be logged,
serialized, stored, and later replayed for debugging or testing
purposes.
In the method removeCard you are modifying the state:
removeCard(board, cardName) {
let deck = this.state.deck;
//This is just a reference, not a clone
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
//Here you are modifying cards, which is a pointer to this.state.deck.cards
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
//Here you are modifying cards, which is a pointer to this.state.deck.cards
i.quantity -= 1;
}
}
});
//... more stuff
}
One concept you might be missing is that this.state.deck.cards is a reference/pointer to the Array's memory position. You need to clone it if you want to mutate it.
One solution could be to clone the original array instead:
removeCard(board, cardName) {
let deck = this.state.deck;
//Here you are cloning the original array, so cards references to a totally different memory position
let cards = Object.assign({}, this.state.deck.cards);
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
i.quantity -= 1;
}
}
});
//... more stuff
}
Hope it helps you.

Resources