Array split React Hooks - reactjs

I want to split an array into two arrays.
The problem is main array which I want to split into two is coming from server.And I need to wait until it loads.
Here my code.
This is useSafeFetch custom Hook which is responsible to fetch data (by the way this is working fine just paste here to show you all code)
const useSafeFetch = (url) => {
const [data, setData] = useState([]);
const [customUrl] = useState(url);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(_ => {
let didCancel = false;
const fetchData = async () => {
if(didCancel === false){
setIsError(false);
setIsLoading(true);
}
try {
const result = await axios(customUrl);
if(didCancel === false){
setData(result.data);
}
} catch (error) {
if(didCancel === false){
setIsError(true);
}
}
if(didCancel === false){
setIsLoading(false);
}
};
fetchData();
return () => {
didCancel = true;
};
}, []);
return {
data,
isLoading,
isError,
}
}
I try to write a function which return a two independent array
export default _ => {
const {data,isLoading,isError} = useSafeFetch(`my api`);
useEffect(_ => {
console.log(data); // length 11
const mainA = splitTwoArrays(data);
console.log("progress",mainA.progressHalf); //length 5
console.log("circle", mainA.circleHalf); //length 1
});
const splitTwoArrays = mainArr => {
const half = mainArr.length >>> 1;
let progressHalf = mainArr.splice(0, half);
let circleHalf = mainArr.splice(half, mainArr.length);
console.log(mainArr);
return {
progressHalf,
circleHalf,
}
}
return (
//do something with data
)
}
This is not worked correctly.
As you can see main data length is 11 but function splitTwoArrays split arrays with wrong way. progressHalf length is 5 another circleHalf is 1.But circleHalf need to 6.
Next try:
using useEffect
export default _ => {
const {data,isError,isLoading} = useSafeFetch(`my api`);
const [progressHalf,setProgressHalf] = useState([]);
const [newArr,setNewArr] = useState([]);
const [half,setHalf] = useState(0);
useEffect(_ => {
setHalf(data.length >>> 1);
setNewArr(data);
const partArr = newArr.slice(0, half);
setProgressHalf([...progressHalf, ...partArr]);
})
return (
//do something with data
)
}
This gets into infinity loop when I uncomment this part setProgressHalf([...progressHalf, ...partArr]);.
I try to give useEffect some dependency but unfortunately this also won't work.

I solve this on my own.
const { data } = useSafeFetch("https://jsonplaceholder.typicode.com/users");
const [copiedData, setCopiedData] = useState([]);
const [halfArr, setHalfArr] = useState([]);
const [secHalf, setSecHalf] = useState([]);
const [half, setHalf] = useState(0);
useEffect(
_ => {
setCopiedData([...data]);
setHalf(data.length >>> 1);
setHalfArr([...copiedData.slice(0, half)]);
setSecHalf([...copiedData.slice(half, copiedData.length)]);
},
[data, half]
);
console.log(halfArr);
console.log(secHalf);
And in the end you get two array which created from main data you get from server.
Codesandbox

Related

Map data on runtime after post request

I have three apis in all. GetAssets is the first, followed by assetsOptionsList and getAssetsLibrary. The issue I'm having is that when I post the data on getAssetsLibrary, I want to be able to present it on get Assets at runtime.Everything is working fine but i want to show assets on runtime.
I'm setting the runTime state true on get request but the problem is it works only for one time.Second time, it does not map on runtime. Actually, i want to know is there any alternative so that i can achieve the goal.
In the below code the one function is getting the assets. And i want to run the one function when the post request successfully sent.
const [images, setImages] = useState([]);
const [assetOptions, setAssetOptions] = useState([]);
const [faqOpened, setToggleFaq] = useState(false);
const [runTime, setRunTime] = useState(false)
const [assetID, setAssetID] = useState()
const [isLoading, setIsLoading] = useState(false);
const handleForm = (e) => {
const index = e.target.selectedIndex;
const el = e.target.childNodes[index]
const option = el.getAttribute('id');
setAssetID(option)
}
const formHandler = (e) => {
e.preventDefault()
let formData = new FormData();
formData.append('media', e.target.media.files[0]);
formData.append('assetListId', assetID)
formData.append('name', e.target.name.value);
console.log(Object.fromEntries(formData))
const res = axios.post('api/asset-library',
formData
).then((response) => {
showSuccessToaster(response?.data?.message)
setRunTime(true)
setToggleFaq(false)
})
.catch((error) => {
showErrorToaster(error?.response?.data?.message)
})
}
const showSuccessToaster = (response) => {
return uploadToasterSuccess.show({ message: response });
}
const showErrorToaster = (error) => {
return uploadToasterError.show({ message: error });
}
const one = async () => {
setIsLoading(true)
const data = await axios.get('api/assets').then((res) => {
return res?.data?.data
})
setImages(data)
setIsLoading(false)
}
const two = async () => {
const data = await axios.get('/api/asset-list').then((res) => {
return res?.data?.data
})
setAssetOptions(data)
}
useEffect(() => {
one()
two()
}, [runTime]);

Stop invoking custom hook on first render

I started having fun with custom hooks recently. I am mostly using them to fetch from api. The thing is that since I cannot really put useFetchLink inside of functions or useEffect i dont know how to prevent it from fetching after website first render. I could put some ifs in the hook but isn't there any other way?
***component***
export default function LinkShortener({ setLinkArr }) {
const [nextLink, setNextLink] = useState();
const inputRef = useRef(null);
const handleClick = () => {
setNextLink(inputRef.current.value);
};
const { shortLink, loading, error } = useFetchLink(nextLink);
useEffect(() => {
setLinkArr((prev) => [
...prev,
{
id: prev.length === 0 ? 1 : prev[prev.length - 1].id + 1,
long: nextLink,
short: shortLink,
},
]);
inputRef.current.value = "";
}, [shortLink, error]);
return (
<LinkShortenerContainer>
<InputContainer>
<LinkInput ref={inputRef} type="text" />
</InputContainer>
<Button
size={buttonSize.medium}
text={
loading ? (
<Loader />
) : (
<FormattedMessage
id="linkShortener.shortenItBtn"
defaultMessage="Shorten It !"
/>
)
}
onClick={handleClick}
></Button>
</LinkShortenerContainer>
);
}
***hook***
const useFetchLink = (linkToShorten) => {
const [shortLink, setShortLink] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const fetchLink = async () => {
setLoading(true);
try {
const response = await fetch(
`https://api.shrtco.de/v2/shorten?url=${linkToShorten}`
);
if (response.ok) {
const data = await response.json();
setShortLink(data.result.short_link);
} else {
throw response.status;
}
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchLink(linkToShorten);
}, [linkToShorten]);
const value = { shortLink, loading, error };
return value;
};```
Why not using directly fetchLink function and calling it whenever you need inside the component? I would change the hook in this way without useEffect inside
const useFetchLink = (linkToShorten) => {
const [shortLink, setShortLink] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const fetchLink = async () => {
setLoading(true);
try {
const response = await fetch(
`https://api.shrtco.de/v2/shorten?url=${linkToShorten}`
);
if (response.ok) {
const data = await response.json();
setShortLink(data.result.short_link);
} else {
throw response.status;
}
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
const value = { shortLink, loading, error, fetchLink };
return value;
};
Generally speaking - the standard way to avoid useEffect from running of 1st render is to use a boolean ref initialized with false, and toggled to true after first render - see this answer.
However, in your case, you don't want to call the function if linkToShorten is empty, even if it's not the 1st render, so use an if inside useEffect.
const useFetchLink = (linkToShorten) => {
const [shortLink, setShortLink] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const fetchLink = useCallback(async (linkToShorten) => {
setLoading(true);
try {
const response = await fetch(
`https://api.shrtco.de/v2/shorten?url=${linkToShorten}`
);
if (response.ok) {
const data = await response.json();
setShortLink(data.result.short_link);
} else {
throw response.status;
}
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
if(linkToShorten) fetchLink(linkToShorten);
}, [fetchLink, linkToShorten]);
const value = { shortLink, loading, error };
return value;
};

Axios function is running twice for some reason in react

Im not sure why but it seems that my axios function is running twice is there any way I could circumvent this? I have a couple useState methods above but I am still confused why it is basically running everything twice
here is the code that is running twice in react
function App() {
let num = [];
num[0] = 0;
if (localStorage.getItem("winstreak") === null) {
localStorage.setItem("winstreak", JSON.stringify(num));
} else {
}
let winstreak = JSON.parse(localStorage.getItem("winstreak"));
let array = JSON.stringify(localStorage.getItem("winstreak"));
let streaks = winstreak.reduce(
function (res, n) {
if (n) res[res.length - 1]++;
else res.push(0);
return res;
},
[1]
);
let streak = Math.max.apply(Math, streaks);
var current = localStorage.getItem("gamesplayed", 0);
var correct = localStorage.getItem("correctgames", 0);
localStorage.getItem("winstreak", winstreak);
const [victory, setVictory] = useState("");
const [seconds, setseconds] = useState();
const [minutes, setminutes] = useState();
const [hours, sethours] = useState("");
const [useHint, setHint] = useState(false);
const [gamestatus, setGameStatus] = useState(false);
const [Attempts, SetAttempts] = useState(0);
const [divnum, setDiv] = useState(1);
const [complete, setComplete] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [buttonpopup, setbuttonpopup] = useState(false);
const [tweets, setTweet] = useState();
const [sharebut, setSharebut] = useState(false);
const [answer, setAnswer] = useState();
const [answer2, setAnswer2] = useState();
getTweet();
function getTweet() {
axios
.get("https://tweetleserver.herokuapp.com/")
.then((res) => {
setTweet(res.data.data.id);
setAnswer(res.data.includes.users[0].name);
setAnswer2(res.data.includes.users[0].username);
console.log("test");
return;
})
.catch((err) => {
console.log(err);
return;
});
}
}
I am calling this function only once but I am still confused as to why it could be doing this
just move the getTweet() inside useEffect will solve the infinit loop (whether you have it) because it's not nice practice if you call it directly in the body of the functional component.
Also i would prefer to have one setState , so one state instead of having multi states .
I mean instead of :
setTweet(res.data.data.id);
setAnswer(res.data.includes.users[0].name);
setAnswer2(res.data.includes.users[0].username);
you can write this :
setSomeState(prevState => ({
...prevState,
tweet: res.data.data.id
answer: res.data.includes.users[0].name,
answer2: res.data.includes.users[0].username
}));
and the state will be :
const [someState, setSomeState] = useState({
tweet: "",
answer: "",
answer2: ""
});

React. How to make function with state?

I'm learning React Native and can't understand how to make function or component instead of repeated part of my code:
export const PostScreen = () => {
const postId = 1
/* THIS PART IS REPEATED IN MANY FILES */
const [data, setData] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState(null);
const fetchData = async () => {
setError(null)
setLoader(true)
try {
const data = await Api.get(`http://localhost/api/posts/${postId}`)
if (data.success == 1) {
setData(data.data)
}
else {
setError(data.error)
}
}
catch(e) {
console.log(e)
setError('Some problems')
}
finally {
setLoader(false)
}
}
useEffect(() => {
fetchData()
}, [])
if (loader) {
return <Loader />
}
if (error) {
return <View><Text>{error}</Text></View>
}
/*END>>> THIS PART IS REPEATED IN MANY FILES */
return (
<View><Text>{data.title}</Text></View>
)
}
The problem is that fetchData is working with state. I found, how to do it with Context, but I don't wont to use it. Is there any way to do clear code without Context?
So, why not make your own hook, then?
Write the hook in a dedicated module:
function useMyOwnHook(props) {
const {
postId,
} = props;
const [data, setData] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState(null);
const fetchData = async () => {
setError(null)
setLoader(true)
try {
const data = await Api.get(`http://localhost/api/posts/${postId}`)
if (data.success == 1) {
setData(data.data)
}
else {
setError(data.error)
}
}
catch(e) {
console.log(e)
setError('Some problems')
}
finally {
setLoader(false)
}
}
useEffect(() => {
fetchData()
}, [])
const render = loader
? <Loader />
: error
? <View><Text>{error}</Text></View>
: null;
return {
data,
render,
}
}
At that point, the component will be written as follows:
export const PostScreen = () => {
const postId = 1
const {
render,
data,
} = useMyOwnHook({ postId });
return render ?? (
<View><Text>{data.title}</Text></View>
)
}

How to fix react paginate problem by moving from page to another

I set up a logic to create pagination in react using data that I fetched from an endpoint API, which contains 5 objects that I displayed in this UI, Then I created a function pageCount that I implemented into my react paginate component, when I click to move to the second page it moved to the last page, next, and previously they didn't work either.
I research a lot to fix the problem but I didn't find any solutions?
Where this problem comes from?
Thank you
function App() {
//states
const [yogaCourses, setYogaCourses] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [levels, setLevels] = useState([]);
const [level, setLevel] = useState('');
const [pageNumber, setPageNumber] = useState(0);
//Paginate react
const coursePerPage = 2;
// const PageVisited = numberPage * coursePerPage;
const pageCount = Math.ceil(yogaCourses.length / coursePerPage);
console.log(pageCount);
//Track changes on each numberPage and display the data
useEffect(()=> {
setYogaCourses(yogaCourses.slice(pageNumber * 2 , (pageNumber + 1) * 2))
},[pageNumber]);
//To the next Page
const pageChange = ({selected}) => {
setPageNumber(selected);
}
//levelChangeHandler
const levelChangeHandler = ({value}) => {
setLevel(value);
}
//Filter by Levels // stateless
const filterLevels = (level) => {
return yogaCourses.filter((singleLevel)=> level ? singleLevel.level === level : true);
}
//Function to fetch the data from the API
const GetCourses = async () => {
const response = await axios.get(url)
const {data} = response;
return data;
}
//UseEffect to run the function on every render
useEffect(()=> {
const GetCoursesYoga = async () => {
const result = await GetCourses();
setYogaCourses(result);
setLevels(Array.from(new Set(result.map((result)=> result.level))));
}
GetCoursesYoga();
}, []);
//check if the we got response
useEffect(()=> {
if(yogaCourses.length > 0) {
setIsLoading(false);
}
}, [yogaCourses])
if(isLoading) {
return (
<Loading/>
)
}
else {
return (
<main>
<div className="title">
<h2>YOUR PRACTICE REIMAGINED</h2>
</div>
<LevelsFilter levels={levels} onChange={levelChangeHandler}/>
<YogaCourses yogaCourses= {(filterLevels(level)).slice(0, 2)}/>
<ReactPaginate
previousLabel = {"Previous"}
nextLabel = {"Next"}
pageCount = {pageCount}
onPageChange= {pageChange}
/>
</main>
);
}
}
export default App;

Resources