this is my code which I have to combined two history components in one. I am trying to combined both using if else loop and it runs as well. but in both icon it will show the all outdated processes.
useEffect(() => {
getVersions('');
}, []);
useEffect(() => {
if (selected_process && selected_process.id) {
if (selected_process.root_version) {
getVersions(selected_process.root_version.id.toString());
} else {
getVersions(selected_process.id.toString());
}
}
}, [props.selected_process]);
const getVersions = (id: string) => {
if (selected_process) {
const url = `${props.location.search}?version=${id}`;
props.searchOutdatedProcessesAction(url, (res: any) => {
res.data.objects.length > 0 && setFetchedProcesses(res.data.objects);
});
}
if (outdated_process) {
const url = `?outdated=true&limit=0&order_by=-date_created`;
props.searchOutdatedProcessesAction(url, (res: any) => {
res.data.objects.length > 0 && setFetchedProcesses(res.data.objects);
});
}
};
const getOutDatedVersions = () => {
return fetchedProcesses.filter((i: Process) => i.id != selected_process?.id);
};
const renderList = () => {
if (fetchedProcesses) {
return (
<div className={styles.tableWrapper}>
<Stack className={styles.table}>
<DetailsList
items={getOutDatedVersions(selected_process=true, outdated_process=true)}
columns={columnsList}
setKey="none"
layoutMode={DetailsListLayoutMode.justified}
isHeaderVisible={true}
selectionMode={SelectionMode.none}
/>
</Stack>
</div>
);
}
return <></>;
};
*************************************************************************************************************************************************************************************************************************************************************************************************************+
I have merged separated useEffect function for both processes using if else loop. Just added in getVersion function with outdated processes and added return in getoutdatedversions function.
useEffect(() => {
if (selected_process && selected_process.id) {
if (selected_process.root_version) {
getVersions(selected_process.root_version.id.toString());
} else {
getVersions(selected_process.id.toString());
}
} else {
getVersions('');
}
}, [props.selected_process]);
const getVersions = (id: string) => {
if (selected_process) {
const url = `${props.location.search}?version=${id}`;
props.searchOutdatedProcessesAction(url, (res: any) => {
res.data.objects.length > 0 && setFetchedProcesses(res.data.objects);
});
} else {
const url = `?outdated=true&limit=0&order_by=-date_created`;
props.searchOutdatedProcessesAction(url, (res: any) => {
res.data.objects.length > 0 && setFetchedProcesses(res.data.objects);
});
}
};
const getOutDatedVersions = () => {
if (selected_process) {
return fetchedProcesses.filter((i: Process) => i.id != selected_process?.id);
} else {
return fetchedProcesses.filter(() => outdated_process);
}
};
Related
i am new, and am getting acquainted with i reactjs and nodejs, i am writing a website in which i want to write a function to get the current login information of the user through redux, and then after then assign the obtained user value to react-select, then from react-select, we select that user to perform the assignment of new data to the database from reactjs. However, I have not been able to get the logged in user information through redux. This is my code, anyone have any ideas? Thanks very much
here is my FE(Reactjs code):
here is DoctorManageSchedule.js:
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import './ManageSchedule.scss';
import Select from 'react-select';
import * as actions from "../../../store/actions";
import { CRUD_ACTIONS, LANGUAGES, dateFormat } from '../../../utils';
import DatePicker from '../../../components/Input/DatePicker';
import moment from 'moment';
import { toast } from "react-toastify";
import _ from 'lodash';
import { saveBulkScheduleDoctor, getScheduleDoctorById } from '../../../services/userService';
import DetailDoctor from './DetailDoctor';
class DoctorManageSchedule extends Component {
constructor(props) {
super(props);
this.state = {
arrDoctor: [],
selectedDoctor: {},
currentDate: '',
rangeTime: [],
minDate: moment().calendar(),
}
}
async componentDidMount() {
this.props.fetchDoctor(this.props.match.params.id);
this.props.fetchAllScheduleTimes();
}
async componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.doctor !== this.props.doctor) {
let dataSelect = this.buildDataInputSelect(this.props.doctor)
this.setState({
arrDoctor: dataSelect
})
}
if (prevProps.allScheduleTime !== this.props.allScheduleTime) {
let data = this.props.allScheduleTime;
if (data && data.length > 0) {
data = data.map(item => ({ ...item, isSelected: false }))
}
this.setState({
rangeTime: data
})
}
}
buildDataInputSelect = (inputData) => {
let result = [];
let { language } = this.props;
if (inputData && inputData.length > 0) {
inputData.map((item, index) => {
let object = {};
let labelEn = `${item.lastName} ${item.firstName}`;
let labelVi = `${item.firstName} ${item.lastName}`;
object.label = language === LANGUAGES.VI ? labelVi : labelEn;
object.value = item.id;
result.push(object)
})
}
return result;
}
handleChangeSelect = async (selectedOption) => {
this.setState({ selectedDoctor: selectedOption });
}
handleOnChangeDatePicker = (date) => {
this.setState({
currentDate: date[0]
})
}
handleClickBtnTime = (time) => {
let { rangeTime } = this.state;
if (rangeTime && rangeTime.length > 0) {
rangeTime = rangeTime.map(item => {
if (item.id === time.id) item.isSelected = !item.isSelected;
return item;
})
this.setState({
rangeTime: rangeTime
})
}
}
handleSaveSchedule = async () => {
let { rangeTime, selectedDoctor, currentDate } = this.state;
let result = [];
if (!currentDate) {
toast.error("Invalid date!");
}
if (selectedDoctor && _.isEmpty(selectedDoctor)) {
toast.error("Invalid selected doctor! ");
console.log('check doctor: ', this.state)
return;
}
let formatedDate = new Date(currentDate).getTime();
if (rangeTime && rangeTime.length > 0) {
let selectedTime = rangeTime.filter(item => item.isSelected === true);
if (selectedTime && selectedTime.length > 0) {
selectedTime.map((schedule, index) => {
let object = {};
object.doctorId = selectedDoctor.value;
object.date = formatedDate;
object.timeType = schedule.keyMap;
result.push(object);
})
} else {
toast.error("Invalid selected time! ");
return;
}
}
let res = await saveBulkScheduleDoctor({
arrSchedule: result,
doctorId: selectedDoctor.value,
formatedDate: formatedDate
})
if (res && res.errCode === 0) {
toast.success("Save Infor succeed!");
} else {
toast.error("error saveBulkScheduleDoctor ");
console.log('error saveBulkScheduleDoctor >>> res: ', res)
}
console.log('bao phuc check result: ', result);
console.log('check res: saveBulkScheduleDoctor : ', res);
}
render() {
let { rangeTime, arrDoctor } = this.state;
console.log("check doctor:", arrDoctor)
let { language } = this.props;
let today = new Date(new Date().setDate(new Date().getDate()));
return (
<div className="manage-schedule-container">
<div className="m-s-title">
<FormattedMessage id="manage-schedule.title"></FormattedMessage>
</div>
<div className="container">
<div className="row">
<div className="col-6 form-group">
<label>
<FormattedMessage id="manage-schedule.choose-doctor" /> </label>
<Select
value={this.state.selectedDoctor}
onChange={this.handleChangeSelect}
options={this.state.listDoctors}
/>
</div>
<div className="col-6 form-group">
<label>
<FormattedMessage id="manage-schedule.choose-date" /> </label>
<DatePicker
value={this.state.currentDate}
className="form-control"
onChange={this.handleOnChangeDatePicker}
minDate={today}
/>
</div>
<div className="col-12 pick-hour-container">
{rangeTime && rangeTime.length > 0 &&
rangeTime.map((item, index) => {
return (
<button className={item.isSelected === true ?
"btn btn-schedule active" : "btn btn-schedule"}
key={index} onClick={() => this.handleClickBtnTime(item)}>
{language === LANGUAGES.VI ? item.valueVi : item.valueEn}
</button>
)
})}
</div>
<div className="col-12">
<button className="btn btn-primary btn-save-schedule"
onClick={() => this.handleSaveSchedule()}>
<FormattedMessage id="manage-schedule.save" />
</button>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
language: state.app.language,
isLoggedIn: state.user.isLoggedIn,
doctor: state.admin.doctor,
allScheduleTime: state.admin.allScheduleTime,
};
};
const mapDispatchToProps = dispatch => {
return {
fetchDoctor: () => dispatch(actions.fetchDoctorStart()),
fetchAllScheduleTimes: () => dispatch(actions.fetchAllScheduleTimes())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(DoctorManageSchedule);
and here is my actionTypes.js:
const actionTypes = Object.freeze({
FETCH_DOCTOR_SUCCESS: 'FETCH_DOCTOR_SUCCESS',
FETCH_DOCTOR_FAILED: 'FETCH_DOCTOR_FAILED',
})
export default actionTypes;
and here is my adminActions.js:
export const fetchDoctorStart = id => () => {
return async (dispatch, getState) => {
try {
let res = await getScheduleDoctorById(id);
if (res && res.errCode === 0) {
dispatch({
type: actionTypes.FETCH_DOCTOR_SUCCESS,
dataDoctor: res.data
})
} else {
toast.error("Failed to fetch doctor");
dispatch(fetchDoctorFailed());
}
} catch (e) {
toast.error("Failed to fetch doctor");
dispatch(fetchDoctorFailed());
console.log("check fetch doctor failed: ", e);
}
}
};
export const fetchDoctorSuccess = (data) => ({
type: actionTypes.FETCH_DOCTOR_SUCCESS,
doctor: data
})
export const fetchDoctorFailed = () => ({
type: actionTypes.FETCH_DOCTOR_FAILED,
})
here is my adminReducer.js:
case actionTypes.FETCH_DOCTOR_SUCCESS:
state.doctor = action.dataDoctor;
return {
...state,
}
case actionTypes.FETCH_DOCTOR_FAILED:
state.doctor = [];
return {
...state,
}
here is my userService.js:
const getScheduleDoctorById = (inputId) => {
return axios.get(`/api/get-schedule-doctor-by-id?id=${inputId}`)
}
here is my BE(Nodejs code):
here is web.js:
router.get('/api/get-schedule-doctor-by-id', doctorController.getScheduleById);
here is doctorController.js:
let getScheduleById= async (req, res) => {
try {
let infor = await doctorService.getScheduleById(req.query.id);
return res.status(200).json(infor);
} catch (e) {
console.log(e);
return res.status(200).json({
errCode: -1,
errMessage: 'Error from the server'
})
}
}
here is doctorService.js:
let getScheduleById = (inputId) => {
return new Promise(async (resolve, reject) => {
try {
if (!inputId) {
resolve({
errCode: 1,
errMessage: 'Missing required parameter!'
})
} else {
let data = await db.User.findOne({
where: {
id: inputId
},
attributes: {
exclude: ['password']
},
include: [
{ model: db.Allcode, as: 'positionData', attributes: ['valueEn', 'valueVi'] },
{
model: db.Doctor_Infor,
attributes: {
exclude: ['id', 'doctorId']
}
},
],
raw: false,
nest: true
})
if (data && data.image) {
data.image = new Buffer(data.image, 'base64').toString('binary');
}
if (!data) data = {};
resolve({
errCode: 0,
data: data
})
}
} catch (e) {
reject(e);
}
})
}
when i run the app i only get the available time slots from the database, but no info about who is logged in, i checked the network tab, but it seems the api gets the user info via redux do not run. Or does anyone have a way to do it other than using redux, that the user (doctor) can set his own schedule and save it to the database? However, I don't know why, please comment, thanks a lot
My issue is that when I start a call for the first time I receive remote stream and the loadMetaData() works fine, but when I close the call and try to call the same peerId I receive the remote stream but the loadMetadata() doesn't emit any events.
MainContainer.tsx
import React, { useState, useEffect } from "react";
// #ts-ignore
import { Peer } from "peerjs";
import decodeJWT from "jwt-decode";
import callStates from "../helpers/callStates";
import { CALL_EVENTS } from "../helpers/callEvents";
import VideoBox from "./InCallView";
import OutgoingCallView from "./OutgoingCallView";
import IncomingCallView from "./IncomingCallView";
import StandbyView from "./StandbyView";
import { Box } from "#mui/material";
export interface Contacts {
firstName: string;
lastName: string;
peerId: string;
}
function MainContainer() {
const [callState, setCallState] = useState(callStates.standby);
const [contactsList, setContactsList] = useState<Contacts[]>([]);
const [callerState, setCallerState] = useState<"receiver" | "caller">();
const resetCallState = () => {
setCallState(callStates.standby);
};
useEffect(() => {
peer.on("connection", (conn: any) => {
conn.on("data", async (data: { event: string }) => {
console.log("EVENT TYPE:: ", data.event);
if (data.event === CALL_EVENTS.CALL_REQUEST) {
setCallerState("receiver");
if (callState === callStates.standby) {
setCallState(callStates.incomingCall);
}
}
if (data.event === CALL_EVENTS.ANSWERED_CALL) {
setCallerState("caller");
setCallState(callStates.inCall);
}
if (data.event === CALL_EVENTS.REJECT_CALL) {
resetCallState();
}
if (data.event === CALL_EVENTS.END_CALL) {
resetCallState();
}
});
});
// eslint-disable-next-line
}, [peer]);
const handleCall = async () => {
console.log("%c Call sent! ", "background: #222; color: #bada55; font-size: 25px");
const conn = await peer.connect(alaaId);
console.log({ conn });
setCallState(callStates.outgoingCall);
conn.on("open", () => {
console.log("%c connection opened! ", "background: #222; color: #bada55");
conn.send({ event: CALL_EVENTS.CALL_REQUEST });
});
};
const answerCall = async () => {
await setCallState(callStates.inCall);
const conn = peer.connect(alaaId);
conn.on("open", () => {
conn.send({ event: CALL_EVENTS.ANSWERED_CALL });
});
};
const rejectCall = () => {
const conn = peer.connect(alaaId);
conn.on("open", () => {
conn.send({ event: CALL_EVENTS.REJECT_CALL });
resetCallState();
});
};
const endCall = () => {
const conn = peer.connect(alaaId);
conn.on("open", () => {
resetCallState();
conn.send({ event: CALL_EVENTS.END_CALL });
});
};
const hangupCall = () => {
const conn = peer.connect(alaaId);
console.log({ conn });
conn.on("open", () => {
conn.send({ event: CALL_EVENTS.HANGUP_CALL });
resetCallState();
});
};
return (
<>
<Box sx={{ display: callState === callStates.standby ? "none" : "block" }}>
{callState === callStates.inCall && <VideoBox callState={callerState} peer={peer} peerId={alaaId} endCall={endCall} />}
{callState !== callStates.standby && callState !== callStates.inCall && (
<React.Fragment>
{callState === callStates.outgoingCall && <OutgoingCallView hangupCall={hangupCall} />}
{callState === callStates.incomingCall && <IncomingCallView answerCall={answerCall} rejectCall={rejectCall} />}
</React.Fragment>
)}
</Box>
{callState === callStates.standby && <StandbyView handleCall={handleCall} contactsList={contactsList} />}
</>
);
}
export default MainContainer;
InCallView.tsx
import { useDebugValue, useEffect, useState } from "react";
function useUserMedia(constraints: any) {
const [stream, setStream] = useState<MediaStream | null>();
const [error, setError] = useState();
useDebugValue({ error, stream });
useEffect(() => {
console.log("inside main useeffect");
let canceled = false;
navigator.mediaDevices.getUserMedia(constraints).then(
(stream) => {
if (!canceled) {
console.log("%c inside canceled condition", "font-size:60px");
setStream(stream);
}
},
(error) => {
if (!canceled) {
setError(error);
}
}
);
return () => {
canceled = true;
setStream(null);
};
}, [constraints]);
useEffect(
() => () => {
console.log("inside cancel useeffect");
if (stream)
stream.getTracks().forEach((track) => {
track.stop();
});
},
[stream]
);
return { error, stream };
}
export default useUserMedia;
useMediaStream hook
import { useState, useEffect } from "react";
export function useUserMedia() {
const [mediaStream, setMediaStream] = useState<MediaStream>();
useEffect(() => {
async function enableVideoStream() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
frameRate: { exact: 15, ideal: 15 },
echoCancellation: true,
noiseSuppression: true,
width: { min: 300, max: 640 },
height: { min: 300, max: 480 },
},
});
setMediaStream(stream);
} catch (err) {
console.log("Video error: ", err);
}
}
if (!mediaStream) {
enableVideoStream();
} else {
return function cleanup() {
mediaStream.getTracks().forEach((track) => {
track.stop();
});
};
}
// eslint-disable-next-line
}, [mediaStream]);
return mediaStream;
}
I trie many solutions but non of them works with me
I'm using React v17. I have a ContentEditable div which has the ability to mention a variable using the Tribute package.
Currently, I cannot place the cursor in front of the Tribute span tags.
The span is not editable. Even if it was, placing the cursor in front of it just results in editing the span tag, can't add text before the tag.
Dependencies:
"react": "^17.0.2",
"react-contenteditable": "^3.3.5",
"tributejs": "^5.1.3",
Demo:
Related code:
const placeHolderRegex =
/<span placeholder="(\w+)" preview="([\w ]*)" original=["|']([\w{} ]*|<a[^<]*<\/a>)["|']>([^>]*|<a[^<]*<\/a> ?)<\/span>/gi
const contenteditableRegex = /[ ]*contenteditable=["|']false["|']*/gi
const linePlaceholderRegex = /{{ (\w+) }}/gi
const transformMentionToPlaceholder = (content: string, type: string) => {
content = content.replace(contenteditableRegex, '')
content = content.replace(/"/gi, '"')
if (type === TemplateTypes.Mail) {
return content.replace(placeHolderRegex, `<span placeholder="$1" preview="$2" original='$3'>$3</span>`)
}
if (type === TemplateTypes.Line) {
return content
.replace(placeHolderRegex, `$3`)
.replace(/<(\/)?div>/g, '')
.replace(/<br>/g, '\n')
}
return content
}
const transformContentToMention = useCallback(
(content: string, type = TemplateTypes.Mail) => {
content = content?.replace(contenteditableRegex, '')
switch (type) {
case TemplateTypes.Mail:
return content?.replace(placeHolderRegex, (_, p1, p2, p3) => {
return `<span placeholder="${p1}" preview="${p2}" original='${p3}' contenteditable="false">${t(p1)}</span>`
})
case TemplateTypes.Line:
return content
?.replace(linePlaceholderRegex, (_, p) => {
return `<span placeholder="${p}" preview="${p}" original="{{ ${p} }}" contenteditable="false">${t(
p
)}</span>`
})
.replace(/\n/g, '<br>')
default:
return ''
}
},
[t]
)
let savedRange: Range
const Editor = ({ placeholders }: Props) => {
const { t } = useTranslation('template')
const {
field: { onChange, onBlur, value },
fieldState: { error }
} = useController({
name: 'content',
rules: {
validate: () => {
const isValid = editorRef.current.innerText.trim().length
return !isValid ? (t('validationContentRequired') as string) : true
}
}
})
const editorRef = useRef(null)
const tributeRef = useRef<Tribute<TemplatePlaceholder>>()
const initTribute = useCallback(
(editorElm: HTMLElement) => {
tributeRef.current = new Tribute({
values: placeholders,
requireLeadingSpace: false,
lookup: 'preview',
containerClass: 'suggestion-container',
menuContainer: document.getElementById('root'),
noMatchTemplate: function () {
return '<span style:"visibility: hidden;"></span>'
},
selectTemplate: function (item: TributeItem<TemplatePlaceholder>) {
return item
? `<span
placeholder="${item.original.name}"
preview="${item.original.preview}"
original='${item.original.original}'
contenteditable="false"
>${t(item.original.name)}</span>`
: ''
},
menuItemTemplate: function (item: TributeItem<TemplatePlaceholder>) {
return item ? t(item.original.name) : ''
}
})
tributeRef.current.attach(editorElm)
},
[placeholders, t]
)
useEffect(() => {
const editorElm = editorRef.current
if (!editorElm) return undefined
if (placeholders && !tributeRef.current) {
initTribute(editorElm)
}
return () => {
if (tributeRef.current) {
tributeRef.current.detach(editorElm)
tributeRef.current = null
}
}
}, [placeholders, initTribute])
const saveCaretPosition = () => {
savedRange = RangeUtils.getCaretPosition()
}
return (
<>
<div className={classNames('editor__wrapper editor', { 'editor--error': !!error })}>
<div className='editor__content'>
<ContentEditable
className='w-full h-full outline-none'
innerRef={editorRef}
html={value || ''}
onKeyDown={event => {
if (event.key === 'Enter') {
document.execCommand('insertLineBreak')
event.preventDefault()
}
}}
onChange={(event: ContentEditableEvent) => {
onChange(event.target.value)
}}
onBlur={() => {
saveCaretPosition()
onBlur()
}}
onPaste={handlePaste}
spellCheck={false}
contentEditable={true}
/>
</div>
</div>
{!!error && <ErrorMessage>{error.message}</ErrorMessage>}
</>
)
}
export default React.memo(Editor)
Here I'm working on AutoComplete and Auto fill of react.
I'm trying to convert it to react hooks as I have written all of my code is in hooks only.
I've to some level converted it to hooks based as per my understanding. But I'm not able to completely convert it.
Original code
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
item: {
code: "",
name: "",
unit: "",
rate: ""
},
cursor: 0,
searchItems: []
};
this.autocomplete = this.autocomplete.bind(this);
this.handleKeyup = this.handleKeyup.bind(this);
this.handleKeydown = this.handleKeydown.bind(this);
this.handleListKeydown = this.handleListKeydown.bind(this);
this.selectItem = this.selectItem.bind(this);
this.handleChange = this.handleChange.bind(this);
}
autocomplete(evt) {
let text = evt.target.value;
fetch(`https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`)
.then((res) => res.json())
.then((data) => {
this.setState({ searchItems: data });
});
}
handleKeyup(evt) {
if (evt.keyCode === 27) {
this.setState({ searchItems: [] });
return false;
}
}
handleKeydown(evt) {
const { cursor, searchItems } = this.state;
// arrow up/down button should select next/previous list element
if (evt.keyCode === 38 && cursor > 0) {
this.setState((prevState) => ({
cursor: prevState.cursor - 1
}));
} else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
this.setState((prevState) => ({
cursor: prevState.cursor + 1
}));
}
if (evt.keyCode === 13) {
let currentItem = searchItems[cursor];
if (currentItem !== undefined) {
const { name, code, rate, unit } = currentItem;
this.setState({ item: { name, code, rate, unit }, searchItems: [] });
}
}
if (evt.keyCode === 8) {
this.setState({ item: { name: "", code: "", rate: "", unit: "" } });
}
}
selectItem(id) {
const { searchItems } = this.state;
let selectedItem = searchItems.find((item) => item.code === id);
const { code, name, unit, rate } = selectedItem;
this.setState({ item: { code, name, unit, rate } });
this.setState({ searchItems: [] });
}
handleListKeydown(evt) {
console.log(evt.keyCode);
}
handleChange(evt) {
this.setState({ item: { [evt.target.name]: evt.target.value } });
}
render() {
const { searchItems, cursor, item, handleChange } = this.state;
const { code, name, unit, rate } = item;
return (
<div className="container mt-3">
<h1 className="h2 text-center">Autocomplete Example</h1>
<div className="form-group">
<label htmlFor="autocomplete">Item Name </label>
<input
type="text"
id="autocomplete"
onChange={this.autocomplete}
onKeyUp={this.handleKeyup}
onKeyDown={this.handleKeydown}
value={name}
className="custom-input form-control"
/>
{searchItems.length > 0 && (
<ul className="list-group">
{searchItems.map((item, idx) => (
<li
className={
cursor === idx
? "active list-group-item"
: "list-group-item"
}
key={idx}
onClick={() => this.selectItem(item.code)}
onKeyDown={(evt) => this.handleListKeydown(evt, item.code)}
>
{item.name}
</li>
))}
</ul>
)}
</div>
</div>
);
}
}
export default App;
Link to original code: https://codepen.io/regexp/details/RwPNaLe
Using hooks
Here is the code that I tried to convert to hooks.
import React, { useState } from "react";
export default function FunctionName(props) {
const [item, setItem] = useState({
vendorNameData: invoiceDetail[0].invoiceData.vendor,
vendorAccountData: invoiceDetail[0].invoiceData.vendaAccount,
vendorAddressData: invoiceDetail[0].invoiceData.vendorAddress
});
const [cursor, setCursor] = useState(0);
const [searchItems, SetSearchItems] = useState([]);
function AutoComplete(evt) {
let text = evt.target.value;
console.log(text);
fetch(`https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`)
.then((res) => res.json())
.then((data) => {
SetSearchItems(data);
});
}
function HandleKeyUp(evt) {
if (evt.keyCode === 27) {
SetSearchItems([]);
return false;
}
}
function HandleKeyDown(evt) {
// const [cursor, setCursor] = useState();
// const [searchItems, SetSearchItems] = useState()
if (evt.keyCode === 38 && cursor > 0) {
setCursor((cursor) => ({ cursor: cursor + 1 }));
} else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
setCursor((cursor) => ({ cursor: cursor + 1 }));
}
if (evt.keyCode === 13) {
let currentItem = searchItems[cursor];
if (currentItem !== undefined) {
const {
vendorNameData,
vendorAccountData,
vendorAddressData
} = currentItem;
setItem({ vendorNameData, vendorAccountData, vendorAddressData });
SetSearchItems([]);
}
}
if (evt.keyCode === 8) {
setItem({
vendorNameData: "",
vendorAccountData: "",
vendorAddressData: ""
});
}
}
function SelectItem(id) {
const [searchItems, SetSearchItems] = useState();
let selectedItem = searchItems.find((item) => item.code === id);
const {
vendorNameData,
vendorAccountData,
vendorAddressData
} = selectedItem;
setItem({ vendorNameData, vendorAccountData, vendorAddressData });
SetSearchItems([]);
}
function HandleListKeyDown(evt) {
console.log(evt.keyCode);
}
function HandleChange(evt) {
setItem({ item: { [evt.target.name]: evt.target.value } });
}
}
It would be really helpful to point me out where I'm lagging. I've tried my best but this is what I could come up with.
Any help would really be appreciated.
I've 'traduced' and cleaned up your original component, resulting as follows: (please see notes below)
import React, {useState, useCallback} from 'react';
function Input() {
const [item, setItem] = useState({
code: '',
name: '',
unit: '',
rate: ''
});
const [cursor, setCursor] = useState(0);
const [searchItems, setSearchItems] = useState([]);
const autocomplete = useCallback((evt) => {
const text = evt.target.value;
fetch(
`https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`
)
.then((res) => res.json())
.then((data) => {
setSearchItems(data);
});
}, []);
const handleKeyup = useCallback((evt) => {
if (evt.keyCode === 27) {
setSearchItems([]);
return false;
}
return true;
}, []);
const handleKeydown = useCallback(
(evt) => {
// arrow up/down button should select next/previous list element
if (evt.keyCode === 38 && cursor > 0) {
setCursor((prevCursor) => prevCursor - 1);
} else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
setCursor((prevCursor) => prevCursor + 1);
}
if (evt.keyCode === 13) {
let currentItem = searchItems[cursor];
if (currentItem !== undefined) {
const {code, name, unit, rate} = currentItem;
setItem({code, name, unit, rate});
setSearchItems([]);
}
}
if (evt.keyCode === 8) {
setItem({code: '', name: '', unit: '', rate: ''});
}
},
[cursor, searchItems]
);
const selectItem = useCallback(
(id) => {
let selectedItem = searchItems.find((item) => item.code === id);
const {code, name, unit, rate} = selectedItem;
setItem({code, name, unit, rate});
setSearchItems([]);
},
[searchItems]
);
const handleListKeydown = useCallback((evt) => {
console.log(evt.keyCode);
}, []);
return (
<div className={'container mt-3'}>
<h1 className={'h2 text-center'}>{'Autocomplete Example'}</h1>
<div className={'form-group'}>
<label htmlFor={'autocomplete'}>{'Item Name'}</label>
<input
type={'text'}
id={'autocomplete'}
onChange={autocomplete}
onKeyUp={handleKeyup}
onKeyDown={handleKeydown}
value={item.name}
className={'custom-input form-control'}
/>
{searchItems.length > 0 && (
<ul className={'list-group'}>
{searchItems.map((item, idx) => (
<li
className={
cursor === idx
? 'active list-group-item'
: 'list-group-item'
}
key={idx}
onClick={() => selectItem(item.code)}
onKeyDown={handleListKeydown}>
{item.name}
</li>
))}
</ul>
)}
</div>
</div>
);
}
export {Input};
useState replaces Class Components State management. It is good practice to split your state into smaller pieces as possible, because new values will completely replace old ones (there is no merging like Class Components this.setState does). Using cursor as an example, do const [cursor, setCursor] = useState(0); to initialize your state (in this case initial state is 0). Then use cursor to use the value and call setCursor(newValue) to update it. Each time you update your state you will most likely trigger a re-rendering.
useCallback allows you to only re-declare a function when its dependencies have changed, thus improving performance. Function dependencies are specified in the second argument array. On each render, React will compare the new values with the old ones and will always return the same function when dependencies have not changed.
The return statement replaces your previous render method.
Follows a working snippet. Please note that OP original code behavior has not been changed.
const {useState, useCallback, StrictMode} = React;
function Input() {
const [item, setItem] = useState({
code: '',
name: '',
unit: '',
rate: ''
});
const [cursor, setCursor] = useState(0);
const [searchItems, setSearchItems] = useState([]);
const autocomplete = useCallback((evt) => {
const text = evt.target.value;
fetch(
`https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`
)
.then((res) => res.json())
.then((data) => {
setSearchItems(data);
});
}, []);
const handleKeyup = useCallback((evt) => {
if (evt.keyCode === 27) {
setSearchItems([]);
return false;
}
return true;
}, []);
const handleKeydown = useCallback(
(evt) => {
// arrow up/down button should select next/previous list element
if (evt.keyCode === 38 && cursor > 0) {
setCursor((prevCursor) => prevCursor - 1);
} else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
setCursor((prevCursor) => prevCursor + 1);
}
if (evt.keyCode === 13) {
let currentItem = searchItems[cursor];
if (currentItem !== undefined) {
const {code, name, unit, rate} = currentItem;
setItem({code, name, unit, rate});
setSearchItems([]);
}
}
if (evt.keyCode === 8) {
setItem({code: '', name: '', unit: '', rate: ''});
}
},
[cursor, searchItems]
);
const selectItem = useCallback(
(id) => {
let selectedItem = searchItems.find((item) => item.code === id);
const {code, name, unit, rate} = selectedItem;
setItem({code, name, unit, rate});
setSearchItems([]);
},
[searchItems]
);
const handleListKeydown = useCallback((evt) => {
console.log(evt.keyCode);
}, []);
return (
<div className={'container mt-3'}>
<h1 className={'h2 text-center'}>{'Autocomplete Example'}</h1>
<div className={'form-group'}>
<label htmlFor={'autocomplete'}>{'Item Name'}</label>
<input
type={'text'}
id={'autocomplete'}
onChange={autocomplete}
onKeyUp={handleKeyup}
onKeyDown={handleKeydown}
value={item.name}
className={'custom-input form-control'}
/>
{searchItems.length > 0 && (
<ul className={'list-group'}>
{searchItems.map((item, idx) => (
<li
className={
cursor === idx
? 'active list-group-item'
: 'list-group-item'
}
key={idx}
onClick={() => selectItem(item.code)}
onKeyDown={handleListKeydown}>
{item.name}
</li>
))}
</ul>
)}
</div>
</div>
);
}
ReactDOM.render(
<StrictMode>
<Input />
</StrictMode>,
document.getElementById('root')
);
.active {
color: #ff0000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id='root' />
This is what I've been trying right now, but it keeps rendering the same page after the first switch.
I tried to follow the react lifecycle to figure it out, but it doesn't work as I intended.
I want it to start from RepoPage -> TimerPage -> ClockPage -> RepoPage and so on.
How can I fix it?
EDIT:
const REPO_PAGE = '5_SECONDS';
const COUNTDOWN_PAGE = '15_SECONDS';
const TIME_PAGE = '15_SECONDS';
const RepoComponent = () => (
<div>REPO</div>
);
const SprintCountComponent = () => (
<div>TIMER></div>
);
const DateTimeComponent = () => (
<div>CLOCK</div>
);
class App extends Component {
constructor(props) {
super(props);
this.state = {
repoTitles: [],
userData: [],
commitData: [],
recentTwoData: [],
currentPage: REPO_PAGE,
imgURL: ""
};
}
componentDidUpdate() {
const {currentPage} = this.state;
const isRepoPage = currentPage === REPO_PAGE;
const isTimePage = currentPage === TIME_PAGE;
if (isRepoPage) {
this._showDateTimePageDelayed();
} else if (isTimePage) {
this._showCountDownPageDelayed();
} else {
this._showRepoPageDelayed();
}
}
componentDidMount() {
this._showCountDownPageDelayed();
};
_showCountDownPageDelayed = () => setTimeout(() => {this.setState({currentPage: COUNTDOWN_PAGE})}, 5000);
_showRepoPageDelayed = () => setTimeout(() => {this.setState({currentPage: REPO_PAGE})}, 5000);
_showDateTimePageDelayed = () => setTimeout(() => {this.setState({currentPage: TIME_PAGE})}, 5000);
render() {
const {currentPage} = this.state;
const isRepoPage = currentPage === REPO_PAGE;
const isTimePage = currentPage === TIME_PAGE;
if(isRepoPage) {
return <RepoComponent/>;
} else if(isTimePage) {
return <DateTimeComponent/>;
} else {
return <SprintCountComponent/>;
}
}
}
You did not have return or else so this._showCountDownPageDelayed() is always executed.
if (isRepoPage) {
this._showDateTimePageDelayed();
} else if(isTimePage) {
this._showRepoPageDelayed();
} else {
this._showCountDownPageDelayed();
}
Using setInterval might give you a cleaner solution.
Edit: Your logic causes it to alternate between RepoPage and TimePage. A quick fix would be:
if (isRepoPage) {
this._showDateTimePageDelayed();
} else if (isTimePage) {
this._showCountDownPageDelayed();
} else {
this._showRepoPageDelayed();
}