React / JS function fails first time after refresh - then works - javascript

I added useEffect to the xml processing function and it fixed it right up.
useEffect(() => {
if (inputValue.length > 50) {
setXmlValue((xmlValue) => loadPoBXml(inputValue));
}
}, [inputValue])
I am rather stumped with this problem.
I am writing a web app that converts a base64 string into a xml file and extracts data from that xml file.
When I first run the app after a npm start or after a browser refresh the function that converts the string does not complete correctly. If I trigger the function a second time, it works, and will continue to work until I do a refresh. I believe the problem lies within the populateData function and have tried to make it work as async, but that made the problem considerably worse.
I have included a screenshot of my console log and the code is detailed below (please note I have for brevity removed some parts of the code):
Buffer.from('anything','base64');
export default function Home() {
// State to store value from the input field
const [xmlValue, setXmlValue] = useState([]);
const [buildValue, setBuildValue] = useState([]);
const [buildData, setBuildData] = useState([]);
const [mainSkill, setMainSkill] = useState("");
const [inputValue, setInputValue] = useState("");
const xmlData = useSelector(state => state)
// redux data management
const dispatch = useDispatch();
function populateData(str) {
let data = ""
if (str.includes("https://")) {
data = httpGet(str)
}
else {
data = str
}
if(typeof xmlData !== "unknown") {
console.log("decode was called")
// get xml data as an array
console.log("pob data was called")
setXmlValue((xmlValue) => loadPoBXml(data))
// populate character data
console.log("handle character called")
handleChar(xmlValue)
// find the main dps skill
findMainSkill(xmlValue)
// populate the store
dispatch(populateGems(xmlValue))
console.log("store loaded")
}
else {
console.log("no decode called")
handleChar(xmlData)
findMainSkill(xmlData)
}
}
function findMainSkill(event) {
// removed code
};
function populateBuildData(data) {
//removed code
}
function handleChar(event) {
console.log(event)
let temp = event.getElementsByTagName('Build')
setBuildValue((buildValue) => temp);
populateBuildData(temp)
};
// Input Field handler
const handleUserInput = (e) => {
setInputValue(e.target.value);
};
// Reset Input Field handler
const resetInputField = () => {
setInputValue("");
setBuildValue([])
setBuildData([])
};
return (
<>
<div className="InsideContent">
<Input className="input1"
placeholder="Paste PoB Code"
name="pobCode"
value={inputValue}
onChange={handleUserInput} />
</div>
<div className="InsideContent">
<Button1 onClick={() => populateData(inputValue)}>Confirm</Button1>
<Button2 onClick={resetInputField}>Reset</Button2>
</div>
{buildValue.length > 0 && (
<div className="card">
<div className="divider"></div>
{buildValue.map((item, i) => (
<ul key={`item${i}`}>
<div className="acend">
<img src={require("../images/" + item.attributes.ascendClassName +".png")}></img>
</div>
<div className="leftInfo">
<p>{item.attributes.ascendClassName} Level: {item.attributes.level}</p>
<div className="leftInfoSmall">
<p>{mainSkill} {buildData.CombinedDPS} DPS</p>
<p>Life: <span className="redText">{buildData.Life}</span> ES: <span className="blueText">{buildData.EnergyShield}</span> Ward: <span className="grayText">{buildData.Ward}</span></p>
</div>
</div>
<div className="rightInfo">
<p>EHP: {buildData.TotalEHP} Max Hit: {buildData.SecondMinimalMaximumHitTaken == 0 ? buildData.PhysicalMaximumHitTaken : buildData.SecondMinimalMaximumHitTaken}</p>
<p>Phy Reduction: {buildData.PhysicalDamageReduction}% Evasion: {buildData.MeleeEvadeChance}% Spell Supression: {buildData.SpellSuppressionChance}%</p>
<p>Resistances: <span className="redText">{buildData.FireResist}</span>/<span className="blueText">{buildData.ColdResist}</span>/<span className="yellowText">{buildData.LightningResist}</span>/<span className="purpleText">{buildData.ChaosResist}</span></p>
</div>
</ul>
))}
</div>
)}
</>
)
}
function loadPoBXml(str) {
var res = decodePoBString(str);
var xml = new XMLParser().parseFromString(res);
console.log("pob data was loaded")
return xml
}
function decodePoBString(str) {
return inflateSync(new Buffer(str, "base64")).toString()
}

Related

Input Type loses focus while typing while working with useState

The input loses its focus when I start typing a character. I saw many StackOverflow answers but none of them is working. I have added unique keys also. What is the reason the code is not working? Without the state, it is working fine. But after adding the state, the input loses the focus.
import React, { useState } from "react";
const Footer = ({ formData }) => {
const [colorsArray, setColors] = useState(["Red", "Green", "Blue", "Yellow"]);
const [sizeArray, setSizes] = useState(["S", "M", "L", "XL"]);
const [sizeInput, setsizeInput] = useState("");
const colorElementRemoveHandler = (indexToRemove) => {
const filteredValue = colorsArray.filter((data, index) => {
return indexToRemove !== index;
});
setColors(filteredValue);
};
const sizeElementRemoveHandler = (indexToRemove) => {
const filteredValue = sizeArray.filter((data, index) => {
return indexToRemove !== index;
});
setSizes(filteredValue);
};
const addColorHandler = (e) => {
let input = e.target.value.toLowerCase();
if (input.length > 2) {
let temp = colorsArray;
temp.push(input);
setColors(temp);
}
};
const addSizeHandler = (e) => {
let input = e.target.value.toUpperCase();
if (input.length > 0) {
let temp = sizeArray;
temp.push(input);
setSizes(temp);
console.log(sizeArray);
}
};
const Test = () => {
return (
<input
type="text"
onChange={(e) => {
setsizeInput(e.target.value);
}}
value={sizeInput}
/>
);
};
const VariantUI = () => {
return (
<div>
<label>Size</label>
<input
id="optionName"
type="text"
placeholder="e.g S, M, L, XL"
onChange={(e) => {
setsizeInput(e.target.value);
}}
value={sizeInput}
/>
</div>
<ul>
{sizeArray.map((data, index) => {
return (
<li key={index}>
{data}
<i onClick={() => {sizeElementRemoveHandler(index);}}></i>
</li>
);
})}
</ul
);
};
return (
<VariantUI formData={formData} />
);
};
export default Footer;
Thanks in advance.
const Footer = ({ formData }) => {
// ..
const VariantUI = () => {
// ...
return (<VariantUI formData={formData} />)
}
You are creating a brand new type of component (VariantUI), in the middle of rendering Footer. This will happen on ever render. Each VariantUi function might have the same text as the previous one, but it's a different function, and thus to react it's a different type of component. Since it's a different type of component, the old one unmounts, and the new one mounts. A newly-mounted <input> does not have focus.
Component types must be defined only once, not on ever render. So VariantUI needs to be moved outside of footer. Since you're currently relying on closure variables, you will need to changes those to props:
const VariantUI = ({
sizeArray, setSizes, sizeInput, setSizeInput, // I might have missed a couple props
}) => {
// ...
}
const Footer = ({ formData }) => {
// ...
return (
<VariantUI
sizeArray={sizeArray}
setSizes={setSizes}
sizeInput={sizeInput}
setSizeInput={setSizeInput}
/>
);
}

How to not real time filter

I'm building on my practice web app and I'm tried to filter the data from fetched data but it filter real time. My question is how to make it not real time, like when searchbar is empty it will fetch all data but when type a text in searchbar it will fetch data from input text.
Here is my code
const { data, loading, error } = useFetch(BASE_URL)
const [search, setSearch] = useState("")
const [inp, setInp] = useState("")
const handleChange = (e) => {
setSearch(e.target.value)
}
if (loading) return <h1> LOADING...</h1>
if (error) console.log(error)
return (
<div className="App" >
<div className="Container">
<label className='header'>Topic</label>
<div className="Container-searchBar">
<input type="Text" value={search} placeholder="Search . . ." onChange={handleChange}/>
</div>
{data.filter((val) => {
if (search === "") {
return val
}
else if (val.tags.includes(search)) {
return val
}
}).map((post) => {
return
.
My return
.
})}
</div>
</div>
);
I'm new to React and JS so sorry for some bad question.
I did not get what you mean by not real-time filtering.
Your approach is okay, if you would like not to apply to filter immediately, you can apply timeout.
const { data, loading, error } = useFetch(BASE_URL);
const [search, setSearch] = useState("");
const [filteredStates, setFilteredStates] = useState([]);
useEffect(() => {
setFilteredStates(data);
const timer = setTimeout(() => {
const filter = data.filter((state) => {
return state.tags.includes(search);
});
setFilteredStates(filter.length ? filter : data);
}, 300);
return () => clearTimeout(timer);
}, [search, data]);
const handleChange = (e) => {
setSearch(e.target.value);
};
if (loading) return <h1> LOADING...</h1>;
if (error) console.log(error);
return (
<div className="App">
<div className="Container">
<label className="header">Topic</label>
<div className="Container-searchBar">
<input
type="Text"
value={search}
placeholder="Search . . ."
onChange={handleChange}
/>
</div>
{filteredStates.map((post) => {
return;
<></>;
})}
</div>
</div>
);
This is not the complete answer you are looking for, but usually you would have to apply conditional trigger for your filter, such as
const onSearchPress = () => {somehowQueryYourData ...... setSearchState(true)}
However, the problem with the info I gave you so far has an issue which I am currenlty facing :
the setSearchState(true) does not get turned off once you trigger the onSearchPress, which in turn creates a real time filter in the end. Hope you can build-on something from this.

Callback set in the state variable of Context Provider will be undefined

I have referred other questions asked here by OPs but none seems to work for me. I have one layout and in that layout there is one toolbar which I am using to submit forms. Now to make that happen I using one FormProvider which is a Context.Provider (wraps layout component) with a state variable which stores function callback to submit a form. Now in the form component (which is loaded conditionally) I am using this setState func from context to assign form submit callback and in the toolbar using state variable from context to call that as a function. The problem I am facing is I always get state variable from context undefined. These are the snippets from my code.
FormProvider
type FormContextProps = {
setOnSubmit: (callable: Function | undefined) => void
assignOnSubmit: Dispatch<any>
setOnCancel: (callable: Function | undefined) => void
submit: (e: any) => void
cancel: () => void
}
const initAuthContextPropsState = {
setOnSubmit: () => { },
assignOnSubmit: () => { },
setOnCancel: () => { },
submit: (e: any) => { },
cancel: () => { },
}
const FormContext = createContext<FormContextProps>(initAuthContextPropsState)
const useTForm = () => {
return useContext(FormContext)
}
const FormProvider: FC = ({ children }) => {
const [onSubmit, assignOnSubmit] = useState<Function | undefined>()
const [onCancel, assignOnCancel] = useState<Function | undefined>()
const setOnSubmit = (callable: Function | undefined) => {
console.log('setOnSubmit', callable)
assignOnSubmit(callable)
console.log('setOnSubmit after', onSubmit)
}
const setOnCancel = (callable: Function | undefined) => {
assignOnCancel(callable)
}
useEffect(() => {
console.log("changed onSubmit"); // this hook is called only on first render
}, [onSubmit])
const submit = (e: any) => {
console.log('form submited', onSubmit) // this is always undefined when I click on save button on toolbar
if (onSubmit) onSubmit(e)
}
const cancel = () => {
if (onCancel) onCancel()
}
return (
<FormContext.Provider value={{ setOnSubmit, assignOnSubmit, setOnCancel, submit, cancel }}>
{children}
</FormContext.Provider>
)
}
Toolbar
const FormToolbar: FC = () => {
const {classes} = useLayout()
const {submit, cancel} = useTForm()
const submitForm = (e: any) => submit(e)
return (
<div className='toolbar' id='kt_toolbar'>
{/* begin::Container */}
<div
id='kt_toolbar_container'
className={clsx(classes.toolbarContainer.join(' '), 'd-flex flex-stack')}
>
<DefaultTitle />
{/* begin::Actions */}
<div className='py-1 d-flex align-items-center'>
{/* begin::Button */}
<button
className='btn btn-sm btn-primary me-4'
onClick={submitForm}
>
Save
</button>
<button
className='btn btn-sm btn-primary'
onClick={cancel}
>
Cancel
</button>
{/* end::Button */}
</div>
{/* end::Actions */}
</div>
{/* end::Container */}
</div>
)
}
EditForm.tsx
const EditForm: React.FC<Props> = () = {
const form = useRef() as React.MutableRefObject<HTMLFormElement>
const { setOnSubmit, assignOnSubmit } = useTForm()
useLayoutEffect(() => {
setOnSubmit(() => { form?.current.dispatchEvent(new Event('submit')) });
console.log('Form changed', form)
}, [form])
return (
<form onSubmit={formik.handleSubmit} ref={form}>
// ...
</form>
);
}
Main Component
function StaffManagement({ user, selectedLanguageId, idMenu }: Props) {
const [editing, setEditing]: [any, Function] = useState(null)
return (
<div className='row'>
<div className="col-lg-4">
<ModuleItemList
className='card-xxl-stretch mb-xl-3'
edit={setEditing}
/>
</div>
<div className="col-lg-8">
{editing && <EditForm
userId={user.id}
menuId={idMenu}
/>}
</div>
</div>
)
}
When using setState, we can do it in two ways,
setState(newState); // directly pass the new state
setState((currentState) => newState); // return the new state from a callback fn
That mean setState can accept a plain state value or a callback function which will return the new state that need to be set as the new state.
When you say,
const setOnSubmit = (callable: Function | undefined) => {
// callable => () => { form?.current.dispatchEvent(new Event('submit')) }
assignOnSubmit(callable)
}
Here React thinks you used the setState(assignOnSubmit) in the 2nd way I mentioned above, so react will call your callback and execute the form?.current.dispatchEvent(new Event('submit')). Since your callable function returns nothing, undefined will assigned to your onSubmit state.
So if you really need to store this function in a state, you have to do it as,
const setOnSubmit = (callable: Function | undefined) => {
assignOnSubmit(() => callable) // React will call the callback and return callable
}
Few Other Tips
Also do not use useLayoutEffect for this task. You can use useEffect and imrpove the performance of your application.
memoize the provider data, otherwise you will trigger unwanted re renders.
const data = useMemo(() => ({
setOnSubmit, assignOnSubmit, setOnCancel, submit, cancel
}), [submit, cancel, setOnCancel, assignOnSubmit, setOnSubmit])
return (
<FormContext.Provider value={data}>
{children}
</FormContext.Provider>
)
State updates are asynchronous. So you can't expect to console.log log the latest state
const setOnSubmit = (callable: Function | undefined) => {
console.log('setOnSubmit', callable)
assignOnSubmit(() => callable)
console.log('setOnSubmit after', onSubmit) // this won't log the latest as assignOnSubmit is async
}
you need to make <FormProvider as a parent of your Toolbar and EditForm to make your context working properly. Based on your code I don't see where you put the <FormProvider, so I'm guessing that you need to put it on your Main Component
Main Component
function StaffManagement({ user, selectedLanguageId, idMenu }: Props) {
const [editing, setEditing]: [any, Function] = useState(null)
return (
<FormProvider>
<div className='row'>
<div className="col-lg-4">
<ModuleItemList
className='card-xxl-stretch mb-xl-3'
edit={setEditing}
/>
</div>
<div className="col-lg-8">
{editing && <EditForm
userId={user.id}
menuId={idMenu}
/>}
</div>
</div>
</FormProvider>
)

How to pass callback function parameters to parent react component?

Just wondering the best way to pass the letterSelected into the useLazyQuery fetchMovies query, so that I don't have to use the static variable of "A". I was hoping there was a way to pass it directly into fetchMovies. useLazyQuery is an apollo query.
const BrowseMovies = () => {
const [fetchMovies, { data, loading}] = useLazyQuery(BROWSE_MOVIES_BY_LETTER, {
variables: {
firstLetter: "A"
}
})
return (
<div className="browserWrapper">
<h2>Browse Movies</h2>
<AlphabetSelect
pushLetterToParent={fetchMovies}
/>
{
data && !loading &&
data.browseMovies.map((movie: any) => {
return (
<div className="browseRow">
<a className="movieTitle">
{movie.name}
</a>
</div>
)
})
}
</div>
)
}
export default BrowseMovies
const AlphabetSelect = ({pushLetterToParent}: any) => {
const letters = ['A','B','C','D','E','F','G','H','I','J','K','L', 'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','#']
const [selectedLetter, setSelectedLetter] = useState("A")
const onLetterSelect = (letter: string) => {
setSelectedLetter(letter.toUpperCase())
pushLetterToParent(letter.toUpperCase())
}
return (
<div className="alphabetSelect">
{
letters.map((letter: string) => {
return(
<div
className={selectedLetter === letter ? 'letterSelectActive' : 'letterSelect'}
onClick={() => onLetterSelect(letter)}
>
{letter}
</div>
)
})
}
</div>
)
}
export default AlphabetSelect
This appears to be a problem solved by Lifting State Up. useLazyQuery takes a gql query and options and returns a function to execute the query at a later time. Sounds like you want the child component to update the variables config parameter.
BrowseMovies
Move firstLetter state BrowseMovies component
Update query parameters/options/config from state
Add useEffect to trigger fetch when state updates
Pass firstLetter state and setFirstLetter state updater to child component
const BrowseMovies = () => {
const [firstLetter, setFirstLetter] = useState('');
const [fetchMovies, { data, loading}] = useLazyQuery(
BROWSE_MOVIES_BY_LETTER,
{ variables: { firstLetter } } // <-- pass firstLetter state
);
useEffect(() => {
if (firstLetter) {
fetchMovies(); // <-- invoke fetch on state update
}
}, [firstLetter]);
return (
<div className="browserWrapper">
<h2>Browse Movies</h2>
<AlphabetSelect
pushLetterToParent={setFirstLetter} // <-- pass state updater
selectedLetter={firstLetter} // <-- pass state
/>
{
data && !loading &&
data.browseMovies.map((movie: any, index: number) => {
return (
<div key={index} className="browseRow">
<a className="movieTitle">
{movie.name}
</a>
</div>
)
})
}
</div>
)
}
AlphabetSelect
Attach pushLetterToParent callback to div's onClick handler
const AlphabetSelect = ({ pushLetterToParent, selectedLetter }: any) => {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#';
return (
<div className="alphabetSelect">
{
letters.split('').map((letter: string) => {
return(
<div
key={letter}
className={selectedLetter === letter ? 'letterSelectActive' : 'letterSelect'}
onClick={() => pushLetterToParent(letter.toUpperCase())}
>
{letter}
</div>
)
})
}
</div>
)
}

Dynamic react hooks to trigger button

I have a react app which getting a data from the server, and I want to add each data a state. Let's see my design app :
NB : the red line are each data that I have requested (the white row is the data attribute).
So each data should be able to be clicked and have own state to show the proposal and laporan akhir attribute. I'm using React hooks, so how am I supposed to do that?
this is my code so far :
function RekapAngkatan({
auth,
getProposalRecapByYear,
getReportRecapByYear,
posts,
StickyNav
}) {
const postingan = posts.posts
const blankPanduan = {id: "", value: false};
const [panduan, setPanduan] = React.useState([{...blankPanduan.value}])
let content;
React.useEffect(() => {
if (auth.isAuthenticated) {
// Server request
const getProposals = async () => await getProposalRecapByYear()
const getReports = async () => await getReportRecapByYear()
getProposals()
getReports()
}
}, [auth])
if (loading) {
content = <Loading />
} else {
// trying to dynamically setState
setPanduan([
...panduan,
{
id: postingan.data.length,
value: false
}
])
content = postingan.data.map((post, i) => {
<Section bg="transparent" margin="0 1.5em" padding="0 2em" key={i}>
<div className="info-color">
{post.value}
<FakeButton
bg="#8C489F"
type="button"
onClick={() => setPanduan([...panduan, {...!blankPanduan}])}
>
Caret Down
</FakeButton>
{/* showing attributes*/}
<Attribute show={panduan.value}>
{post.amount}
</Attribute>
</div>
</Section>
})
}
return content
}

Categories

Resources