Dynamically updating the title component for a Material UI Tooltip - tooltip

I am using a React Material UI Tooltip component. Specifically :
render() {
const {comment} = this.props;
...
<Tooltip title={this.handleFlaggedComment(comment)}>
<IconButton className={classes.tooltipIconButton} aria-label="Reported" href="">
<ReportedIcon className={classes.reportedIcon} />
</IconButton>
</Tooltip>
...
}
The handleFlaggedComment is implemented as :
handleFlaggedComment = (comment) => {
return (
<Fragment>
<div>
REPORTED COUNT : {comment.reportedCount}
</div>
</Fragment>
)
}
Lets also assume there is a mapStateToProps which is implemented as :
mapStateToProps(state) {
return {
comment: state.commentsList.currentComment
}
}
When the reportedCount goes from 1 to say 3, and I hover over the icon to show the tooltip, I would expect it to show 3 as the reportedCount. However it only shows 1 , which was the initial value
Is there a way to make the tooltip title update and show the updated comment, specifically the correct reportedCount ?
let us also assume that some UI action is triggering the reportedCount to increase. An example, is clicking on an icon that increments the reportedCount by 1 and it is updating state in redux for 'currentComment'

Related

ios mobile version of app on chrome and safari - input doesn't focus on first click

I have a search bar that has an customized input component. It works fine on another fixed nav but in the body of the page, it doesn't autofocus on single click.
I understand there are a ton of stack overflows and believe me, i've tried many of them (non jquery).
The problem: Single click on a div doesn't autofocus the input.
The device: All ios mobile (Safari and Chrome).
It does not happen on android mobile or on any of the desktops - onclick immediately focuses and brings up the keyboard on android mobile.
Here's some code to show how its setup
Input component
<ImportedSearchLIbrary
inputProp={
<InputComponent
autofocus
ref={inputRef}
/>
}
/>
^There is no problem with this component because it works just fine in that nav bar i mentioned.
This is how it is implemented in the body of a page>
const SearchComponent = () => {
const [show, setShow] = useState(false);
const wrapperRef = useRef(null);
const handleClose = () => {
setShow(false);
};
const handleClick = (e) => {
if (e && e.nativeEvent) e.nativeEvent.stopImmediatePropagation();
setShow(true);
const element = document.getElementById('searchID');
const yOffset = -72;
const yPosition = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
const screenWidth = window.innerWidth;
window.scrollTo({
top: yPosition,
behavior: 'smooth',
});
};
useClickOutside(wrapperRef, handleClose, [show]);
return (
<section id='searchId' ref={wrapperRef}>
<Icon iconName="search" className='' />
<div onClick={(e) => handleClick(e)}>
{show ? (
<SearchInput
placeholder='search me'
showsearch={show}
/>
) : (
<p>search me</p>
)}
</div>
{show && (
<Icon iconName="close" onClick={() => handleClose()} />
)}
</section>
);
};
So you can see that when <section> is clicked, show is set to true, and the searchbar AND the close icon show up. On desktop and android mobile (chrome, brave browser, mozilla and safari on desktop), when you click the section, it will show the search bar and auto focus to type.
This image shows how it looks - the red portion is the searchbar, the white background is the section, clickable area.
What I've tried so far>>
I've tried accessing the input ref like>
const inputRef = useRef(null);
<SearchInput
placeholder='search me'
showsearch={show}
inputRef={inputRef}
/>
Then in useEffect, (with and without settimeout)
useEffect(()=> {
setTimeout(() => {
if (show && inputRef) {
inputRef.current.focus();
}
}, 100);
}, [show]);
This didn't throw any errors but it didn't work.
Then, I tried the same thing in the searchinput component so when the component loads, in useEffect I set the focus to the passed ref.
Then I tried this >
document.addEventListener('mousedown', inputRef.current.focus());
These are the main things I've tried.
Now what works, is that if i ALWAYS show the input ie remove the condition show &&, but I don't want that.
Resources I found from stackOverflow but they didn't quite work>>
Can't Set Focus in Safari
ReactJS: How-to set focus to input-element when it enters the DOM?
https://www.quora.com/Regarding-the-mobile-browser-Safari-for-both-the-iPhone-and-iPad-with-JavaScript-how-can-I-launch-the-on-screen-keyboard
If more info is needed, i'd be happy to edit my Question with it.
Any help will be appreciated.
Thanks

Prevent FlatList re-rendering causing performance problems

I'm building a React Native App using Hooks and ES6.
The Home screen fetches some data from API and display it as image galleries.
The Home screen is the parent component which includes a slideshow gallery and a Flatlist as children.
1) Slideshow - image gallery autoplay that run some operations (fetching URL in case user clicks on them) on each image iteration (every 3 secs) - This causes the re-render on the entire parent components and its children but it renders every 3 secs as supposed
2) Flatlist - just a simple Flatlist that takes an array of images - this should only rendered once when the parent component is initially loaded first
The problem
Every 3 seconds a new image is displayed in the slideshow, a function runs to fetch some props for the image displayed, the parent component is re-rendered but there is no need for Flatlist in the second gallery to run again since the array of images is the same as initially loaded (not changed)
This is my Parent code
const getTopTenMovies = (state) => state.moviescreen.popularmovies //fetching data from Redux
const upcomingMovies = (state) => state.moviescreen.upcomingmovies
const MoviesScreen = (props) => {
const topTenMovies = useSelector(getTopTenMovies);
const [imageIndex, setImageIndex] = useState("");
/* useEffect below runs every time imageIndex changes (a new image is displayed every 3 secs) */
useEffect(() => {
setCarouselMovieId(selectedImageItem.id);
setCarouselMovieTitle(selectedImageItem.title);
}, [imageIndex]);
/* user clicks on an image and is taken to another screen
const fetchMovieHandler = async (movieId, movieTitle) => {
props.navigation.navigate({
routeName: "MovieDetail",
params: {
assetId: movieId,
assetName: movieTitle,
},
});
};
return (
<ImageGallery
data={topTenMovies}
currentImage=setImageIndex(index)
...
/>
<View style={styles.containerRow}>
<FlatList
horizontal={true}
data={upcomingMovies}
initialNumToRender={5}
windowSize={10}
removeClippedSubviews={true}
keyExtractor={(item) => item.id.toString()}
renderItem={(itemData) => (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
onAssetSelection={fetchMovieHandler}
/>
)}
/>
</View>
</View>
)
}
The component renders the individual images within the Flatlist and passes the parameters (movieId, movieTitle) to the fetchMovieHandler function.
HomeItem code below...
class HomeItem extends React.PureComponent {
render() {
const assetHandler = async () => {
this.props.onAssetSelection(this.props.id, this.props.title);
};
console.log("HomeItem is called");
return (
<TouchableOpacity onPress={assetHandler}>
<View>
<Image
key={this.props.id}
source={{
uri: `https://*******${this.props.poster}`,
}}
/>
</View>
</TouchableOpacity>
);
}
}
export default React.memo(HomeItem);
Every 3 seconds the HomeItem is called and I see 10 log entries (one for each image and render - upcomingMovies has 10 images). From the UI everything looks fines since the images dont seems to change probably because I've defined the HomeItem as PureComponent and using React.memo(HomeItem) but it is causing performance issues since I'm getting the following error:
VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc
I tried to include the Flatlist inside the HomeItem component but the problem persists.
Following further research and thanks to #landorid suggestion I figured out that I need control the renderItem of the FlatList by declaring it in a separate function with a useCallback hook and dependency on my data source.
Tried to include Flatlist in a separate PureComponent my the re-rendering kept happening.
The ideal solution is to change this code
renderItem={(itemData) => (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
onAssetSelection={fetchMovieHandler}
/>
)}
to something like this
.....
const renderRow = ({ itemData }) => {
return (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
title={itemData.item.title}
onAssetSelection={fetchMovieHandler}
/>
);
};
keyExtractor = (item) => item.id.toString();
return (
<FlatList
horizontal={true}
data={upcomingMovies}
initialNumToRender={5}
windowSize={10}
removeClippedSubviews={true}
keyExtractor={keyExtractor}
renderItem={renderRow}
/>
and then wrapping the renderRow() function in a useCallback hook.
The problem is that my data source data={upcomingMovies} is an array of objects with 2 levels and I need to use itemData.item.title to retrieve the title value for example.
While it works in my current renderItem settings it won't work when I use it in an external function as in
const renderRow = ({ itemData }) => {
return (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
title={itemData.item.title}
onAssetSelection={fetchMovieHandler}
/>
);
};
I simply get, "item" variable not available
Any suggestion on how to modify the function?
If I were you I would change the following:
Don't place inline functions in FlatList (keyExtractor, renderItem)! Place it to separated function.
Use simple Component extension instead of PureComponent and compare your props with `shouldComponentUpdate. (I think the passed function called onAssetSelection causes the re-render. Check it.)
You don't need to use React.memo because it is for functional components.

Cannot turn autoCorrect to {false} in react native TextInput

On my TextInput change text, I detect whether the user pushed the # button for mentions.
onChangeText(text){
const suggestTrigger = text.match(/\B#[A-Za-z0-9]*$/i) //grab "#" trigger
const searchQuery = (suggestTrigger && suggestTrigger.length > 0) ? suggestTrigger[0] : null;
this.setState({
searchQuery: searchQuery
})
}
Then, in my render, I do:
<TextInput
autoCapitalize={this.state.searchQuery ? "none" : "sentences"}
autoCorrect={this.state.searchQuery ? false : true}
onChangeText={this.onChangeText}
/>
However, even when I do this, the autoCorrect does not turn off.
I still see this:
This is causing problems because oftentimes the system replaces the entire mention with a different word altogether.
How can I turn autoCorrect and autoCapitalize off when the user pushes the # button?
'
I have even tried rendering an entirely different , but the behavior remains.
TL;DR: you should close and re-launch your keyboard after the TextInput autoCorrect toggling value.
Buddy, this is not your fault, I had the same issue on autoFocus of react native TextInput component. I set a state name for the TextInput editable prop and then with the pressing pencil button I change the editable props. The designer told me after the TextInput made editable the cursor should be focused, so I use the isEditable state for autoFocus prop, see below:
state = {
isEditable: false
};
handleEdit = () => {
const { isEditable } = this.state;
this.setState({ isEditable: !isEditable });
};
<TextInput
autoFocus={isEditable}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
/>
Then I press the edit button and it turns to:
But it is not focused and the Keyboard didn't launch, I sought and found this link, the TextInput does not change/update some of its props after componentDidMount. ☹️. Also, it is not different in iOS or Android, both of them has this issue, I used ref to focus on this TextInput after the isEditable state made true. see following code:
<TextInput
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
componentDidUpdate() {
const { isEditable } = this.state;
if (isEditable) {
this.input.focus();
}
}
And your case:
Absolutely you can not use ref because the autoCorrect should render with react native and it is not like focus() and blur() so JavaScript cannot access to it.
I make a test shape for your case, I create another button like a star for toggling autoCorrect value alongside my edit button. the filled star means autoCorrect is true and the lined star means autoCorrect is false, now see the test area code and view:
state = {
isEditable: false,
correct: true
};
handleCorrect = () => {
const { correct } = this.state;
this.setState({ correct: !correct });
};
<TextInput
autoCorrect={correct}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
In the above photo, it is so clear the autoCorrect rendered as true, so it is enabled:
When I write a wrong Persian word the auto-correction show its suggestion, now time to press the star button:
Wow, the autoCorrection is false in the above situation but still we see the auto-correction of the cellphone. it is just like autoFocus it is rendered in the first render and after it, the TextInput could not change/update its props. suddenly I press edit button:
And I press the edit button again, so surely, you realized the autoCorrect is false now, ok now see what I saw:
The autoCorrect remained false by my double pressing edit button and now the auto-correction of device disappears completely. I don't know it is a bug or my bad understanding but I realized in this test area, for update autoCorrect value, we should do something after changing its value to close the iPhone keyboard and then re-launch it. the main thing that TextInput has issued is the launched keyboard.
For my test, when I pressed the edit button the editable prop of the TextInput is changed to false and the keyboard is closed, so when I pressed the edit button again, the TextInput get focused and keyboard re-launched with new autoCorrect value. This is The Secret.
Solution:
You should do something, to close and open again the iOS keyboard with the new autoCorrect value. for the test case that I wrote for your question, I decided to do a hybrid innovative solution, using ref and the callback of setState:
handleCorrect = () => {
const { correct } = this.state;
this.input.blur(); //-- this line close the keyboard
this.setState({ correct: !correct },
() => {
setTimeout(() => this.input.focus(), 50);
//-- above line re-launch keyboard after 50 milliseconds
//-- this 50 milliseconds is for waiting to closing keyborad finish
}
);
};
<TextInput
autoCorrect={correct}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
And after pressing the star button the keyboard close and re-launch and the auto-correction disappear completely.
Note: obviously, I summarized some other codes like destructuring and writing class or extends and etc for better human readability.
The problem isn't in your code completely(except Regex part which didn't work in my device) but how the React Native renders Keyboard.
I created a sample that along with your initial code also changes backgroundColor of the screen.
You will find that color changes instantly when '#' is entered whereas the keyboard doesn't.
Unless you reload the keyboard. For this, if you un-comment the code it dismisses keyboard once and once you tap back on textInput the new Keyboard without autoCorrect and autoCapitalize is shown.
state = {
searchQuery: null,
isFocused: true,
}
constructor(props) {
super(props);
this.onChangeText = this.onChangeText.bind(this);
}
onChangeText = (val) => {
const suggestTrigger = val.match(/#[A-Za-z0-9]*$/i) //grab "#" trigger
const searchQuery = (suggestTrigger && suggestTrigger.length > 0) ? suggestTrigger[0] : null;
// Un comment this to reload
// if(searchQuery && this.state.isFocused) {
// this.setState({
// isFocused: false
// });
// Keyboard.dismiss();
// // this.myTextInput.focus()
// }
this.setState({
searchQuery: searchQuery
})
}
render() {
const { searchQuery } = this.state
return (
<View style={[styles.container,
{
backgroundColor: searchQuery ? "red": "green"}
]}>
<TextInput
style={{width: 300, height: 50, borderWidth: 1}}
ref={(ref)=>{this.myTextInput = ref}}
autoCapitalize={searchQuery ? "none" : "sentences"}
autoCorrect={searchQuery ? false : true}
onChangeText={this.onChangeText}
/>
</View>
);
}
Since now we know the main cause of error may be a better solution can be reached.
I have 2 suggestions:
First, have you tried using the autoCorrect fallback?
spellCheck={this.state.searchQuery ? false : true}
Second, have you tried with native code (Obj-C / Swift)?
import { Platform, TextInput, View } from 'react-native';
import { iOSAutoCorrect } from './your-native-code';
const shouldWork = Platform.OS === 'ios' ? <iOSAutoCorrect /> : <TextInput
autoCapitalize={this.state.searchQuery ? "none" : "sentences"}
autoCorrect={this.state.searchQuery ? false : true}
onChangeText={this.onChangeText} />
return (<View>{shouldWork}</View>);
In iOSAutoCorrect you should use a UITextView. Then set its proper value depending on your condition:
textField.autocorrectionType = UITextAutocorrectionTypeNo; // or UITextAutocorrectionTypeYes
I have free-coded, thus the code is untested and might contain bugs.

Vaadin 8 using an image as a button. Doesn't fire click event

I'm building a Vaadin 8 app ( first one for me ). I am using the designer to generate the UI. I've added several buttons to the dashboard which should fire a function when clicked. For some reason nothing fires when the image is clicked. Below is all the code that is involved. Can anyone see what I'm doing wrong?
This is the code from the .html file:
<vaadin-horizontal-layout responsive width-full margin>
**<vaadin-image icon="theme://images/properties.png" style-name="my-image-button" responsive alt="" _id="imagePropertyInfo"></vaadin-image>**
<vaadin-image icon="theme://images/occupants.png" responsive alt="" _id="imageOccupants"></vaadin-image>
<vaadin-image icon="theme://images/vendors.png" responsive alt="" _id="imageVendors"></vaadin-image>
</vaadin-horizontal-layout>
Here is the scss
.my-image-button
{
cursor: pointer;
}
Here is the code from the Dashboard UI
public DashboardHomeView( OnCallUI onCallUI )
{
this.onCallUI = onCallUI;
// Make it disabled until a property is selected
**imagePropertyInfo.setEnabled( false );
imagePropertyInfo.setStyleName( "my-image-button" );**
fetchPropertyBasicInfo();
}
protected void fetchPropertyBasicInfo()
{
List<PropertyProfileBasic> listOfPropertyProfiles = new ArrayList<PropertyProfileBasic>( OnCallUI.myStarService.fetchAllPropertyProfileBasicInformation() );
comboBoxGeneric.setCaption( "Select a Property" );
comboBoxGeneric.setItemCaptionGenerator( aProperty -> aProperty.toString() );
comboBoxGeneric.setItems( listOfPropertyProfiles );
comboBoxGeneric.addValueChangeListener( event -> fetchOccupantBasicInfo( event ) );
comboBoxGeneric.focus();
}
protected void fetchOccupantBasicInfo( ValueChangeEvent<PropertyProfileBasic> event )
{
// Fetch all the occupants for the selected property
if( event.getValue().getPropertyNo() != null )
{
// Fetch a list of occupant basic info for the selected property
List<OccupantProfileBasic> listOfOccupantProfiles = new ArrayList<OccupantProfileBasic>( OnCallUI.myStarService.fetchOccupantProfileBasicByPropertyNo( event.getValue().getPropertyNo() ) );
// Clear the existing grid et al
gridContainer.removeAllComponents();
// Add the occupant grid
occupantGrid = new OccupantProfileBasicGrid( listOfOccupantProfiles );
// Show the grid
gridContainer.addComponents( new Label( "Occupants" ), occupantGrid );
// Set the dashboard buttons to enabled now a property is selected
**imagePropertyInfo.setEnabled( true );
// Add the property info button
imagePropertyInfo.addClickListener( e -> fetchPropertyInformation() );**
}
}
protected void fetchPropertyInformation()
{
Notification.show( "Yo!", "You clicked!", Notification.Type.HUMANIZED_MESSAGE );
}
I assume you are using GridLayout. I am recommending another approach. Use Button, and set the button style to be borderless (apparently you want something like that. The icon of the button can be image from your theme, using ThemeResource. "Pseudo code" is something like this:
ThemeResource icon = new ThemeResource("/images/properties.png");
Button imagePropertyInfo = new Button(icon);
imagePropertyInfo.setStyleName(ValoTheme.BUTTON_BORDERLESS);
imagePropertyInfo.addClickListener( e -> fetchPropertyInformation() );
Note also, JavaDoc of Image component says.
"public Registration addClickListener(MouseEvents.ClickListener listener)
Add a click listener to the component. The listener is called whenever the user clicks inside the component. Depending on the content the event may be blocked and in that case no event is fired."
I think it does not like your way of setting image with theme, without using Resource.
If you want to remove the focus highlight of the button, it should be possible via this CSS rule:
.v-button-link:after {
content: none;
}
Also it is worth of mentioning that Image is not Focusable, while Button is. This means that even that Image can have click listener, it is not reached by keyboard navigation, Button is Focusable and is reached by tabbing etc. So using Button instead of Image makes your application more accessible.

jQuery-ui Tooltip get Title on click

I'm working with jquery-ui. I can create elements with titles and show the titles. However on a click, I would like to take the title and populate another div (this is because touch enabled devices do not have tooltips). I can get a click event, but I can't get the title while in the click event.
$("div").click(function( event ) {
// just to prove that we are entering this event
$("#preShow").html ( Date() );
// show the title
var toolTip = this.attributes.title.value;
$("#show").html ( toolTip );
// just to prove that there was no crash
$("#postShow").html ( Date() );
});
I have also tried using
var toolTip = $(this).attr ("title");
Here is the jsfiddle showing the problem
http://jsfiddle.net/jK5xQ/
The same code works if I create an HTML file and run it in Firefox with a breakpoint at the first line of the click event. Has anyone experienced this?
This is because jQueryUI's Tooltip removes the title and uses it. Try going about it like this...
$(document).ready(function () {
$( document ).tooltip( {
track: true,
content: function() {
return $( this ).attr( "title" );
}
});
$('div').click(function(){
$('#show').html($('#' + $(this).attr('aria-describedby')).children().html());
});
});
DEMO: http://jsfiddle.net/jK5xQ/4/
Let me know if you have any questions!

Resources