Why are tippy react components still rendered when tippy is not visible and component is not in dom tree - tippyjs

When the visible prop turn false, the render element is removed from the DOM, but all react components are still rendering. Why is this?
// my tippy
<Tippy
interactive
visible={isSortOpen}
render={() => <TableSort />}
appendTo={document.body}
onClickOutside={()=>setSortIsOpen(false)}
>
<SomeComponent />
</Tippy>
// my component
const SomeComponent = () => {
useInterval(()=>{
console.log("tick")
}, 300)
return <div id="my-component"/>
}
// useInterval removes the interval on unmount, but in the console,
// "tick" will continue to be printed although `visible` is set to `false`
// AND my `SomeComponent` is no longer in the DOM.
// the component can also be seen in react devtools

Related

Prevent re render of shared ListHeaderComponent

I am working on a social media app where i have a container component that has the following structure
<MyContainer>
<SelectionBar/>
{condition? <FlatListA header={header}/> : <FlatListB header={header}/>}
<MyContainer/>
the selection bar has buttons that determine which FlatList to display for the purpose of this question lets say messages FlatList vs posts FlatList
these two FlatLists have different listeners and data so they need to be their own component but they share the same ListHeaderComponent which is a feature similar to snapchat stories
the problem is when the user switches between two FlatLists the stories flicker because the component is re rendered because its two different FlatLists
the header needs to be inside the flatlist as a ListHeaderComponent because when the user scrolls down the stories should not stick to the top
is there any way to prevent this re rendering?
I've tried React.memo but that did not work
You can prevent re-rendering of same component by using React.memo
You can define your header component and pass it as a prop like:
import { memo } from "react";
import FlatListA from "./FlatListA";
import FlatListB from "./FlatListB";
const header = memo((props) => {
console.log("header render");
return <h1>this is header</h1>;
});
export default function App() {
return (
<div className="App">
<FlatListA header={header} />
<FlatListB header={header} />
</div>
);
}
and you can use it in your FlatList components like:
import { useState } from "react";
export default function FlatListA(props) {
console.log("flatlista render");
const [toggle, setToggle] = useState(false);
return (
<div>
<props.header />
FlatlistA {toggle}
<button onClick={() => setToggle(!toggle)}>toogle state</button>
</div>
);
}
You can take a look at this example codesandbox and click buttons to change state and see console outputs that it does not re-render header components.

React-bootstrap carousel don't start inside react-visibility-sensor when change interval value dynamically

I'm using React-Bootstrap Carousel component inside react-visibility-sensor component, like this:
<VisibilitySensor>
{
({ isVisible }) => (
<Carousel
activeIndex={activeIndex}
controls={false}
interval={isVisible ? 3000 : null}
onSelect={this.handleChangeActiveIndex}
fade={fade}
>
{
images
}
</Carousel>
)
}
</VisibilitySensor>
When VisibilitySensor component's changed, isVisible will be changed and interval value will be changed to null (for stop carousel) or 3000.
But, currently, when VisibilitySensor component's changed, isVisible was changed, component was re-render but Carousel not start slide (on isVisible is true).
Is it a my some mistake?
Thanks.

Unit testing using Jest and Enzym in React Native

How can I find element inside component with jest and enzyme?
Let say, I have 1 component parent (Login) and 2 child component (Title and Form), so in component Login, I want to find is there TextInput element inside Form component or something else maybe another element inside Form Component, with jest and enzyme, then how can I get that just with 1 unit testing (Login.test.js)?
This is my login component for the ilustration
<Login>
<Title title='Login Page' />
<Form
email={this.state.email}
password={this.state.password}
/>
</Login>
Title Component
<Text>{this.props.title}</Text>
Form Component
<View>
<TextInput value={this.props.email} placeHolder="Your Email" />
<TextInput value={this.props.password} placeHolder="Your Password" />
</View>
This is my current Login.test.js
import React from 'react';
import { shallow } from 'enzyme';
import Login from './Login';
import renderer from 'react-test-renderer';
describe('Login', () => {
const wrapper = shallow(<Login />);
const instaceOf = wrapper.instance();
it('renders correctly', () => {
const rendered = renderer.create(<Login />).toJSON();
expect(rendered).toBeTruthy();
});
it('should render the Text Input Element', () => {
let form = wrapper.find('Form');
expect(form.find('TextInput"]')).toHaveLength(2);
});
});
When you use enzyme shallow method, you are only rendering the first level of your component.
Therefore, the statement:
const wrapper = shallow(<Login />);
is only rendering the Title and Form components but not their children.
If you want to render all the component tree, you should use mount instead. That being said, the tests for your Login component should only test its first children. If you want to test that Form component renders two TextInput components, you should do so in the unit tests belonging to Form component (not in Login component).

How can I get CKEditor 5 "Link" dialog box to pin to custom DOM element instead of 'document.body'

I'm building a Vue.js web application. I'm using CKEditor in a form that is placed inside a modal window. By design, the user's focus is "trapped" in the modal. In CKEditor, when user clicks the "Link" icon in toolbar, the editor opens a dialog box and attaches the new DOM element to 'document.body'. With respect to the DOM, the "Link" dialog is now outside of trapped focus. The user cannot click or tab his way to the "Link" dialog input.
I dug into the ckeditor5-ui source and found relevant code in balloonpanelview.js. I've unsuccessfully tried to configure CKEditor based on https://ckeditor.com/docs/ckeditor5/latest/api/module_utils_dom_position-Options.html
In my Vue.js component, I have:
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
...
data: () => ({
editor: ClassicEditor,
editorConfig: {
toolbar: ['bold', 'italic', 'bulletedList', 'numberedList', 'link'],
},
...
})
...
I want the CKEditor "Link" dialog DOM element to be attached to a DOM element id that I specify.
In Vuetify dialog component is required to disable retain-focus
<v-dialog :retain-focus="false" />
There may be much time since you opened the issue. However... This issue was happening to me too. This is happening because Bootstrap modal trap the focus in the active modal. If you're using bootstrap-vue, do this.
In your <b-modal> add the prop no-enforce-focus.
no-enforce-focus is reactive. To properly apply this workaround you can use this prop with a variable, that detects when your CKeditor have focus. If have focus, disable the enforce focus. If doesn't have, restore it. You can apply it by the following way:
<template>
<b-modal
...
:no-enforce-focus="editorFocus">
<ckeditor
...
#focus="toggleEditorFocus(true)"
#blur="toggleEditorFocus(false)"
/>
</b-modal>
</template>
<script>
export default {
...
data () {
return {
editorFocus: false
}
},
methods: {
toggleEditorFocus (val = !this.editorFocus) {
setTimeout(() => {
this.editorFocus = val
}, 10)
}
}
}
</script>
I know the setTimeout is a tricky method, but at least is working now for me.

How to initiate props when rendering Component?

I have a Login Component where I want the user to choose Service from a Service Catalogue. The Picker gets and sets values to redux:
<Picker
selectedValue={this.props.service.id}
onValueChange={itemValue => this.props.setServiceType(itemValue)}>
{service_catalogue.map(service =>
<Picker.Item key={service.id} label={service.id} value={service.id} />
)}
</Picker>
But I don't know how to properly set the initial value. I set the default value in componentDidMount (the first item in the Catalogue), but I think componentDidMount is trigged on update? Is there a lifecycle function that is only triggered on rendering Component in React Native?
componentDidMount() {
this.props.setServiceType(service_catalogue[0].id)
}
So the problem that I'm facing is that even though the user might choose "Instructor" the service becomes service_catalogue[0] = "Cleaner". And if I don't setServiceType on componentDidMount no Picker appears, as this.props.service.id doesn't exist.
You can set default value for the props on the following way:
...
YourComponent.propTypes = {
myState: PropTypes.string,
}
YourComponent.defaultProps = {
myState: 'default value',
};
const mapStateToProps = state = ({
myState: state.myState,
});
export default connect(mapStateToProps)(YourComponent);
More info: https://reactjs.org/docs/typechecking-with-proptypes.html#default-prop-values