Skip to content

Instantly share code, notes, and snippets.

@kaung8khant
Last active June 7, 2023 15:18
Show Gist options
  • Save kaung8khant/490eabd973c86c9d2d1831b459aaa57c to your computer and use it in GitHub Desktop.
Save kaung8khant/490eabd973c86c9d2d1831b459aaa57c to your computer and use it in GitHub Desktop.
Lazy Loading with React, GraphQL and Formik (Phone number input - country code with flag)
import React from "react";
import { gql } from "apollo-boost";
import { Form,Dropdown } from "semantic-ui-react";
import PhonePrefix from "PhonePrefix";
//use graphql for api call
const COUNTRY_QUERY = gql`
{
countries {
id
name
nationality
phone
iso_code_2
}
}
`;
//wrap form with formik for form control
const App = (values)=>{
return <Query query={COUNTRY_QUERY}>
{({ loading, error, data }) => {
let list = data.countries || [];
if (loading) {
return (
<Form.Field width={4} id="phoneField" className="cityCode">
<Dropdown
loading
name={"phoneprefix"}
fluid
className="selectRole boxBorder"
size="small"
icon="angle down"
search
selection
options={dummyData}
/>
</Form.Field>
);
} else {
return (
<Form.Field width={4} id="phoneField" className="cityCode">
<PhonePrefix
list={list}
defaultValue={values.phoneprefix}
text={values.phonePrefixText || ""}
setFieldValue={setFieldValue} //formik (key,value)=>{}
name={"phoneprefix"}
/>
</Form.Field>
);
}
}}
</Query>;
export default App;
import React, { Component } from "react";
import { Dropdown } from "semantic-ui-react";
import _ from "lodash";
import { FlagIcon } from "react-flag-kit";
export default class PhonePrefix extends Component {
constructor(props) {
super(props);
this.state = {
list: this.props.list.slice(0, 20),
alllist: this.props.list,
index: 1,
firstScroll: true
};
this.myRef = React.createRef();
}
componentDidMount() {
document.addEventListener("scroll", this.trackScrolling);
}
componentWillUnmount() {
document.removeEventListener("scroll", this.trackScrolling);
}
checkExist = (list, stateList) => {
if (list.length > 0 && stateList.length > 0) {
if (
_.find(stateList, item => {
return item.id === list[0].id;
})
) {
return false;
} else {
return true;
}
} else {
return false;
}
};
trackScrolling = () => {
let { list, alllist, index } = this.state;
let intNeed = alllist.length % 10;
intNeed = intNeed === 0 ? intNeed : 10 - intNeed;
const wrappedElement = this.myRef.current.ref.current.getElementsByClassName(
"menu"
)[0];
if (this.isBottom(wrappedElement)) {
let bottomIndex = index + 1;
let start = bottomIndex * 10;
let end = bottomIndex * 10 + 10;
let newArray = alllist.slice(start, end);
if (end <= alllist.length + intNeed) {
if (this.checkExist(newArray, list)) {
list.push(...newArray);
list.splice(0, 10);
index++;
this.setState({ list: list, index: index, firstScroll: false });
}
}
} else if (this.isTop(wrappedElement)) {
if (!this.state.firstScroll) {
let topIndex = index - 2;
if (topIndex >= 0) {
let start = topIndex * 10;
let end = topIndex * 10 + 10;
let newArray = alllist.slice(start, end);
if (this.checkExist(newArray, list)) {
list.unshift(...newArray);
end = list.length;
start = end - 10;
let endIndexForAll = Math.floor(alllist.length / 10);
if (intNeed !== 0 && index === endIndexForAll) {
start = end - (10 - intNeed);
}
list.splice(start, end);
index--;
this.setState({ list: list, index: index }, () => {
wrappedElement.scrollTop = 200;
});
}
}
} else {
this.setState({ firstScroll: false });
}
}
};
isBottom(el) {
return el.scrollTop > 400;
}
isTop(el) {
return el.scrollTop === 0 && !this.state.firstScroll;
}
setdata = data => {
this.setState({ alllist: data });
};
handleSearch = (e, data) => {
let alllist = this.props.list;
let result = _.filter(alllist, list => {
return (
_.includes(list.name.toLowerCase(), data.searchQuery) ||
_.includes(list.phone, data.searchQuery) ||
_.includes(list.iso_code_2.toLowerCase(), data.searchQuery)
);
});
let list = result.slice(0, 20);
this.setState({
alllist: result,
list: list,
searchQuery: data.searchQuery,
index: 1
});
};
render() {
let list = [];
let countries = this.state.list;
if (countries) {
countries.map(item => {
let noImgList = ["EH", "AQ", "BQ", "MF", "SH"];
if (
!_.find(noImgList, list => {
return item.iso_code_2 === list;
})
) {
let listItem = {};
listItem["key"] = item.id;
listItem["text"] =
"[" + item.iso_code_2 + "] " + item.name + "( +" + item.phone + ")";
listItem["icon"] = <FlagIcon code={item.iso_code_2} size={20} />;
listItem["value"] = item.id;
listItem["prefix"] = item.phone;
list = [...list, listItem];
return list;
}
return item;
});
}
return (
<Dropdown
ref={this.myRef}
fluid
search
selection
//searchQuery={this.state.searchQuery || ""}
onSearchChange={this.handleSearch}
name={this.props.name}
placeholder="Prefix"
options={list}
className="phoneFlag selectRole boxBorder"
size="small"
icon="angle down"
loading={this.props.loading}
defaultValue={this.props.defaultValue}
text={this.props.text || ""}
onScroll={this.trackScrolling}
onChange={(e, v) => {
let selectedItem = _.find(v.options, item => {
return item.value === v.value;
});
this.props.setFieldValue(
"phonePrefixText",
"+" + selectedItem.prefix
);
this.props.setFieldValue(this.props.name, parseInt(v.value));
}}
/>
);
}
}
@hasogolcu
Copy link

hey. can u share this project with code sandbox please? Regards

@kaung8khant
Copy link
Author

@hasogolcu Sorry, I don't have access to that repo anymore. There are a few things I would do to improve such as using stack for storing the list and divide the code into diferent chunk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment