Typing in React Native - "Binding element 'navigation' implicitly has an 'any' type.ts(7031)" - node.js

not new to programming but new to app development and reactive native. I have been searching for almost four hours to solve this. I have realised there are many questions like this, but this is not a duplicate as any working code has not worked on mine.
I followed this tutorial and VS code gave me errors. Namely, relating to Text and Button imports in the .tsx screen file. The following code is constantly giving me an error:
const HomeScreen = ({ navigation }) => {
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigation.navigate('Profile', { name: 'Jane' })
}
/>
);
};
The error: Binding element 'navigation' implicitly has an 'any' type.ts(7031)
From what I gather this is related to typing in the language and because the type has not been declared, I cannot use the code. The problem is, is that I have followed the tutorial, searched many different websites and a few different tutorials, I have not been able to get this working.
I have tried all of these variations for the first line:
const HomeScreen = ({ navigation.navigate ) => {
const HomeScreen = ({ navigation } : Navigator) => {
const HomeScreen = ({ navigation NavigationContainer}) => {
const HomeScreen = ({ navigation = NavigationAction}) => {
const HomeScreen = ({ }) => {
So I have tried quite a few different variations to try to solve this. I am honestly really stuck, after trying to follow the tutorial and finding no answers online, I really do not know what to do. I am sure there is something very obvious wrong with the syntax but being new to the language I don't know how to spot it. Thank you.

import { StackScreenProps } from '#react-navigation/stack';
...
const HomeScreen = ({ navigation }:StackScreenProps<{Profile: any}>) => {

You have to declare a type for navigation.
const HomeScreen = ({ navigation:: StackNavigationProp<HomeParamList> }) => {}
So in HomeParamList you can define the navigation paths.
export type HomeParamList= {
Home: undefined;
};

Related

Jest/React Testing Library test failing - react router link click not working as expected

I have a failing test but can't work out why. I have react-router links which link to the URL structure: /classes/${weekday}.
Classes component then sets the activeWeekday in context by React Router location which is displayed by the Classes as {activeWeekday} Classes
Functionality works i nthe browser, but for some reason in my tests it's not updating the header so the test is failing.
TestingLibraryElementError: Unable to find an element with the text: /friday classes/i
Can anyone see why? I can't figure it out.
Thks so much in advance.
Update - Here is a codepen replicating the issue.
// Snapshot of the state passed to Classes via context
export const ClassesProvider = ({ children }: ClassesProviderProps) => {
const [activeWeekdayNumber, setActiveWeekdayNumber] = useState<number>(
new Date().getDay()
);
// Classes component
const Classes = () => {
const { activeWeekdayNumber, setActiveWeekdayNumber } = useContext(ClassesContext);
const location = useLocation();
useEffect(() => {
const day = location.pathname.replace("/classes/", "");
const dayIndex = daysOfWeekArray.indexOf(capitaliseFirstLetter(day));
if (dayIndex !== -1) {
setActiveWeekdayNumber(
daysOfWeekArray.indexOf(capitaliseFirstLetter(day))
);
}
}, [location, setActiveWeekdayNumber]);
return (
<>
<h2>{daysOfWeekArray[activeWeekdayNumber]} Classes</h2>
</>
);
};
// failing test - TestingLibraryElementError: Unable to find an element with the text: /friday classes/i
const setup = (value: ClassesContextType) =>
render(
<ClassesContext.Provider value={value}>
<MemoryRouter>
<Classes />
</MemoryRouter>
</ClassesContext.Provider>
);
test("displays the relevant heading when a day of the week link is clicked", () => {
const value = {
activeWeekdayNumber: 3, // wednesday
};
setup(value);
const link = screen.getByRole("link", { name: "Friday" });
fireEvent.click(link);
expect(screen.getByText(/friday classes/i)).toBeInTheDocument();
});
});
The menu list is a styled link:
<li>
<HorizontalMenuLink $active={weekdayNumber === 1} to="/classes/monday">
Monday
</HorizontalMenuLink>
</li>

Update prop for dynamically inserted element

New to react... Really banging my head against it with this one... I'm trying to figure out how to get a dynamically inserted component to update when the props are changed. I've assigned it to a parent state object but it doesn't seem to re-render. I've read that this is what's supposed to happen.
I was using ReactDOM.unmountComponentAtNode to re-render the specific elements I needed to, but it kept yelling at me with red text.
I need to hide "chat.message" unless the user has the authority to see it (server just sends empty string), but I still need to render the fact that it exists, and reveal it should the user get authentication. I'm using a css transition to reveal it, but I really need a good way to update the chat.message prop easily.
renderChats(uuid){
let userState = this.state.userStates.find(user => {
return user.uuid === uuid;
});
const children = userState.chats.map((chat) => {
let ChatReactElement = this.getChatMarkup(chat.cuid, chat.message, chat.status);
return ChatReactElement;
});
ReactDOM.render(children, document.getElementById(`chats-${this.state.guid}-${uuid}`));
}
getChatMarkup() just returns JSX and inserts Props... I feel like state should be getting passed along here. Even when I use a for-loop and insert the state explicitly, it doesn't seem to re-render on changes.
getChatMarkup(cuid, message, status){
return(
<BasicChatComponent
key={cuid}
cuid={cuid}
message={message}
status={status}
/>
);
}
I attempted to insert some code line this:
renderChats(uuid){
let userState = this.state.userStates.find(user => {
return user.uuid === uuid;
});
const children = userState.chats.map((chat) => {
let ChatReactElement = this.getChatMarkup(chat.cuid, chat.message, chat.status);
if(chat.status.hidden)
this.setState({ hiddenChatRE: [ ...this.state.hiddenChatRE, ChatReactElement ] }); // <== save elements
return ChatReactElement;
});
ReactDOM.render(children, document.getElementById(`chats-${this.state.guid}-${uuid}`));
}
and later in my code:
this.state.hiddenChatRE.every(ReactElement => {
if(ReactElement.key == basicChats[chatIndex].cuid){
ReactElement.props = {
... //completely invalid code
}
}
});
The only response I see here is my ReactDOM.unmountComponentAtNode(); approach...
Can anyone point me in the right direction here?
Although perhaps I should be kicking myself, I read up on how React deals with keys on their components. So there's actually a fairly trivial answer here if anyone comes looking... Just call your render function again after you update the state.
In my case, something like:
this.setState(state =>({
...state,
userStates : state.userStates.map((userstate) => {
if(userstate.uuid == basicChats[chatIndex].uuid) return {
...userstate,
chats: userstate.chats.map((chat) => {
if(chat.cuid == basicChats[chatIndex].cuid){
//
return {
cuid: basicChats[chatIndex].cuid,
message: basicChats[chatIndex].message,
status: basicChats[chatIndex].status
}
}
else return chat;
})
}
else return userstate;
})
}));
and then, elsewhere in my example:
this.state.userStates.map((userstate) => {
this.renderChats(userstate.uuid);
});
Other than the fact that I'd recommend using indexed arrays for this example to cut complexity, this is the solution, and works. This is because even though it feels like you'd end up with duplicates (that was my intuition), the uid on the BasicChatComponent itself makes all the difference, letting react know to only re-render those specific elements.

Tiptap how to create a paragraph (p) on Shift-Enter, instead of a br?

Using TipTap, I'm trying to avoid adding a <br />, but create a <p></p> instead, with the focus inside that <p>|</p> when the user hit shift-Enter but I can't make it work.
Here's what I did so far:
new (class extends Extension {
keys () {
return {
'Shift-Enter' (state, dispatch, view) {
const { schema, tr } = view.state
const paragraph = schema.nodes.paragraph
console.log(tr.storedMarks)
const transaction = tr.deleteSelection().replaceSelectionWith(paragraph.create(), true).scrollIntoView()
view.dispatch(transaction)
return true
}
}
}
})()
How can I do this?
I don't know if this is still relevant but as I was looking for the same thing, I found two ways to make this work.
NOTE:
I'm using tiptap v2, if that's not a problem, then:
I overrode the HardBreak extension, since it's the one that use the Shift-Enter keybinding. It looks something like;
const CustomHardBreak = HardBreak.extend({
addKeyboardShortcuts() {
return {
"Mod-Enter": () => this.editor.commands.setHardBreak(),
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And used it like so;
editor = new Editor({
extensions: [
customNewline,
CustomHardBreak,
]
});
Use the default editor command createParagraphNear. E.g this.editor.commands.createParagraphNear()
I tried creating a custom extension from your code and ended up with something similar to the command above, i.e;
export const customNewline = Extension.create({
name: "newline",
priority: 1000, // Optional
addCommands() {
return {
addNewline:
() =>
({ state, dispatch }) => {
const { schema, tr } = state;
const paragraph = schema.nodes.paragraph;
const transaction = tr
.deleteSelection()
.replaceSelectionWith(paragraph.create(), true)
.scrollIntoView();
if (dispatch) dispatch(transaction);
return true;
},
};
},
addKeyboardShortcuts() {
return {
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And added this as an extension in my editor instance.
PS:
They both work, almost exactly the same, I haven't found a difference yet. But there's somewhat of a 'catch' if you would call it that; Both these methods don't work on empty lines/nodes, a character has to be added before the cursor for it to work, any character, even a space.
In TipTap 2.0 I am able to use this custom extension:
const ShiftEnterCreateExtension = Extension.create({
addKeyboardShortcuts() {
return {
"Shift-Enter": ({ editor }) => {
editor.commands.enter();
return true;
},
};
},
});
To make shift + enter behave like enter.
In my case I actually wanted enter to do something different. So I use prosemirror events to set a ref flag on whether shift was pressed. Than I check that flag under the "Enter" keyboard event -- which could be triggered normally or through the shift + enter extension.

React Native, UI Kitten Input custom style on default and onFocus states

I need some help about creating a custom input style on default and onFocus states.
import React, {useState} from 'react';
import {
StyleSheet,
View,
} from 'react-native';
import {
Input,
Layout,
} from '#ui-kitten/components';
export const CustomInputExample = () => {
const [ focusStatus, setFocusStatus ] = useState(false)
const onMouseEnter = () => {
setFocusStatus(true)
}
const onMouseLeave = () => {
setFocusStatus(false)
}
return (
<Layout>
<Input
style={focusStatus ? styles.customStyle : styles.basicStyle}
status={focusStatus ? 'success' : 'basic'}
placeholder='Success'
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
/>
</Layout>
);
};
const styles = StyleSheet.create({
basicStyle: {
borderColor: grey,
},
customStyle: {
borderColor: '#3CB46E'
},
});
But, none of the methods are working in my case. I've tried other InputProps like focus, onTextFieldFocus, onTextFieldBlur...May be I do not know how to use them.
Also this is the error from ts =>
Property 'onMouseEnter' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Readonly<...> & Readonly<...>'.ts(2322)
Any help can be grateful.
onMouseEnter and onMouseLeave will not work unless you build a custom component and then only if you use TouchableWeb. Additionally it will only work on web (No hover events on phone or tablet).
Of course this will require writing your own mapping following UI-Kitten's Create a Custom Component Mapping directions
At last, I've found the solution. I'am writing to help others who uses react-native-ui-kitten.
InputComponent implements WebEventResponderCallbacks interface so =>
export interface WebEventResponderCallbacks {
onMouseEnter?: () => void;
onMouseLeave?: () => void;
onFocus?: () => void;
onBlur?: () => void;
}
I don't know the reason why onMouseEnter, onMouseLeave or any other methods of InputComponent have no use but onFocus and onBlur are working.
You don't need to implement these interfaces in case you follow Branding guide. You can change primary colors everywhere so your app will follow a single source of truth about styling.

React-native and Redux healthy way to call actions on props change

I've been using react-native with redux for a while, and the way i learn to call actions when something change on prop is using the componentWillReceiveProps, but when I use it I need to pass between if's and some times it goes to the wrong if, then I need to add more stuff to prevent it.
Here's an example I have done. I know this is not the best way to do it, but it is what I could think of.
componentWillReceiveProps(newProps) {
if(Object.keys(newProps.selected_product).length > 0) {
if(Object.keys(this.props.current_location).length > 0 || Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
this.props.verifyProductById(newProps.selected_product, newProps.current_location, this.props.token);
} else {
this.props.statusScanner(false);
this._handleNextPage(1);
}
} else if(Object.keys(newProps.historic_product_confirm).length > 0) {
if(newProps.historic_product_confirm.location._id == newProps.current_location._id)
this.props.handleModalConfirmPrice(!this.props.modal_confirmPrice_status)
} else if(newProps.scanResult != "") {
this.props.statusScanner(false);
if(Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
} else {
this._handleNextPage(1);
}
} else {
this._handleNextPage(0);
}
}
What I need is a healthy way to call my actions when the props change.
Edit:
Here i have the full OfferScene and an action file example:
OfferScene:
https://gist.github.com/macanhajc/0ac98bbd2974d2f6fac96d9e30fd0642
UtilityActions:
https://gist.github.com/macanhajc/f10960a8254b7659457f8a09c848c8cf
As mentioned in another answer, componentWillReceiveProps is being phased out, so I would aim for trying to eliminate it where possible. You'll be future-proofing your code and keeping your component logic more declarative and easy to reason about. As someone who has been responsible for (and been frustrated by) lifecycle method abuse like this, here are some things that have helped me.
Remember that when using redux-thunk, along with passing dispatch as the first argument, you can also pass getState as the second. This allows you to access state values in your action logic instead of bringing them into your component's props and adding clutter. Something like:
export const ExampleAction = update =>
(dispatch, getState) => {
const { exampleBool } = getState().ExampleReducer
if (exampleBool) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update
})
}
}
Using async/await in action logic can be a lifesaver when your action depends upon fetched results from an API call:
export const ExampleAction = () =>
async (dispatch, getState) => {
const { valueToCheck } = getState().ExampleReducer
, result = await someAPICall(valueToCheck)
.catch(e => console.log(e))
if (result.length > 0) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update: result
})
}
}
For cases where your component's rendering behavior depends upon certain state values after your state has been updated, I highly recommend reselect. A very basic example would be something like:
component.js
import React, { Component, Fragment } from 'react'
import { connect } from 'react-redux'
import { shouldDisplayItems } from '../selectors'
import MyListviewComponent from './myListview'
class ItemList extends Component {
render() {
const { shouldDisplayItems, items } = this.props
return (
<>
{shouldDisplayItems && <MyListviewComponent items={items} />}
</>
)
}
}
const mapStateToProps = ({ ListItems }) => shouldDisplayItems(ListItems)
export default connect(mapStateToProps)(ItemList)
selectors.js:
(Assuming your ListItems reducer has the params items and visibilityFilter)
import { createSelector } from 'reselect'
export const shouldDisplayItems = createSelector(
[state => state],
({ items, visibilityFilter }) => {
return {
shouldDisplayItems: visibilityFilter && items.length > 0,
items
}
}
)
I should mention that another option would be using higher-order components, but it can be tricky to use this approach before having a good grasp on how to keep too much imperative logic out of your components (I learned this the hard way).
I agree with #AnuragChutani and #Goldy in terms of clarity of the code; break it down some more into more components or functions.
Now after some review of your componentWillReceiveProps function, it is definitely not specific enough to narrow down exactly which prop changes. If any connected redux variable changes, the componentWillReceiveProps function will be invoked each time.
So e.g. if 'token' or 'selected_product' updates, componentWillReceiveProps will be triggered, even though you did not want it to trigger for token updates.
You can use a comparison for a specific variable update in the props.
E.g Using lodash
if(!_.isEqual( nextProps.selected_product, this.props.selected_product ))
// if props are different/updated, do something
Secondly, you can call actions/callbacks in your actions to narrow down navigation.
E.g.
takePicture = (camera, options){
...
//on success
dispatch(handleModalConfirmPrice())
...
}}

Resources