How to use useContext data with properties - reactjs

I'm using useContext in a component page, and it correctly gets datas through useContext in a type of a property.
colorContex.js
import { createContext, useEffect, useState, useContext } from 'react';
// create context object
export const ColorContext = createContext({});
export const ProductsProvider = (props) => {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchAPI() {
const res = await fetch(url);
const posts = await res.json();
setData(posts);
}
fetchAPI();
}, []);
return <ColorContext.Provider value={data}>{props.children}</ColorContext.Provider>;
};
headerDefault.tsx
const colors = useContext(ColorContext);
console.log(colors);
// the data shows up correctly in console log
const colorData = colors.response;
// the error message( the property doesn't exist type {}. )
Google development
the data is correct and a type of property.
How can I get property datas?

The problem here is this line:
export const ColorContext = createContext({});
TypeScript infers the context type from this line and {} does not have a .response property.
To fix this, define the type of your Context:
type ColorContextType = null | {
response: {
result_info: any, // TODO: type this correctly
result_list: any[], // TODO: type this correctly
}
}
export const ColorContext = createContext<ColorContextType>(null);
export const ProductsProvider = (props) => {
const [data, setData] = useState<ColorContextType>(null);

Related

How to pass an argument in a react custom hook in typescript?

I am trying to pass a string value in a custom hook call to the hook file itself. For example, I have a json file I want to pass to a custom hook to get that data, however in my current implementation, I am getting an error of TS2345: Argument of type 'string' is not assignable to parameter of type '{ string: any; }'. even when I specify the prop types. Why is this happening?
Custom hook to dynamically call a JSON file for its data
import axios from 'axios';
import React, { FC } from 'react';
import { useState, useEffect } from 'react'
interface APICallProps {
apiCall: string
}
const useFetchSecurityData = ({ string: apiCall }) => {
const [loading, setLoading] = useState<boolean>(true)
const [data, setData] = useState<null>(null)
useEffect(() => {
fetchData()
}, [])
const fetchData = async () => {
axios.get(apiCall)
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
setLoading(false)
}
return { data, loading }
}
export default useFetchSecurityData
Implementation of custom hook on a page layout
const ChartArea = () => {
const [incidents, setIncidents] = useState([])
const { data, loading } = useFetchSecurityData('incidents.json')
// const { data, loading } = useFetchIncidents()
useEffect(() => {
fetchData()
}, [loading, data])
const fetchData = () => {
try {
let rawData = (data) ?? []
setIncidents(rawData)
} catch (err) {
console.error(err)
}
}
}
{ string: apiCall } is not the correct syntax for typing in this scenario.
{ string: apiCall } is asking to destructure your props for the component, and in props their should be a property called string which you wish to alias as apiCall.
Typing for a hook should look like one of the following:
interface MyReturnType {
data: any;
loading: boolean;
}
interface MyCustomHookFn {
(propA: string, propB: number, propC: Date) => MyReturnType
}
const useMyCustomHook: MyCustomHookFn = (propA, propB, propC) => {
...
}
OR
const useMyCustomHook = (propA: string, propB: number, propC: Date) => {
...
}
Previous examples for components retained just in case someone wants them.
Typing for a component should look something like:
import React, { FC } from 'react';
interface ExampleComponentProps {
apiCall: string
}
const ExampleComponent: FC<APICallProps> = ({ apiCall }) => {
...
}
OR
import React from 'react';
interface ExampleComponentProps {
apiCall: string
}
const ExampleComponent = ({ apiCall }: APICallProps) => {
...
}

How to add an item to an array within context? (React typescript)

I created a context search in my application, where I have an array called "searchPosts". My goal is to send an object from a component into this array in context and thus be able to use it in other components. I would like to create a global state where my object is stored
context
import { createContext } from "react";
export type SearchContextType = {
searchPost: (text: string) => void;
};
export const SearchContext = createContext<SearchContextType>(null!);
provider
import React, { useState } from "react";
import { SearchContext } from "./SearchContext"
export const SearchProvider = ({ children }: { children: JSX.Element }) => {
const [searchPosts, setSearchPosts] = useState([]);
const searchPost = (text: string) => {
}
return (
<SearchContext.Provider value={{searchPost}}>
{ children }
</SearchContext.Provider>
);
}
I created this search function because in theory it should be a function for me to add the item to the array, but I don't know how I could do that.
This is the state that I have in my component called "searchPosts" that I get the object that I would like to pass to my global array. I want to pass the information from this array in this component to my global array in context
const navigate = useNavigate();
const api = useApi();
const [searchText, setSearchText] = useState('');
const [searchPost, setSearchPost] = useState([]);
const handleSearch = async () => {
const posts = await api.getAllPosts();
const mapPosts = posts.filter(post => post.title.toLowerCase().includes(searchText));
setSearchPost(mapPosts);
}
In the component searchPosts, try to import SearchContext and import searchPost function from context component using useContext hook.
import {SearchContext} from './SearchContext'
const {searchPost} = useContext(SearchContext);;
Now, inside your handleSearch function, pass the mapPosts array to searchPost function that you imported from useContext hook, like this:
const handleSearch = async () => {
const posts = await api.getAllPosts();
const mapPosts = posts.filter(post => post.title.toLowerCase().includes(searchText));
setSearchPost(mapPosts);
searchPost(mapPosts);
}
Now, inside your searchPost function inside your provider component, add following code:
const searchPost = (posts: any[]) => {
setSearchPosts(prev => {
return [...prev, ...posts];
})
}
Add the searchPost[] to SearchContextType
import { createContext } from "react";
export type SearchContextType = {
searchPost: (text: string) => void;
searchResult: string[];
};
export const SearchContext = createContext<SearchContextType>(null!);
Create a reducer to manage your dispatch
//const SEARCH_POST = "SEARCH_POST"; in constants.ts
import { SEARCH_POST } from 'constants';
// reducer
export const reducer = (state, action) => {
switch (action.type) {
case SEARCH_POST: {
return { ...state, searchResult: action.value };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
Create the provider
interface InitState {
searchResult: string[];
}
export const SearchProvider = ({ children }: { children: JSX.Element }) => {
const initialState: InitState = {
searchResult: [],
};
const [controller, dispatch] = useReducer(reducer, initialState);
const value = useMemo(() => [controller, dispatch], [controller, dispatch]);
return <SearchContext.Provider value={value}>{children}</SearchContext.Provider>;
};
export const searchPost = (dispatch, value) => dispatch({ type: SEARCH_POST, value });
Now create a custom hook to access the context
export const useSearchState = () => {
const context = useContext(SearchContext);
if (!context) {
throw new Error(
"useSearchState should be used inside the SearchProvider."
);
}
return context;
};
In your component, you can use the above to access the state.
const [controller, dispatch] = useSearchState();
const {searchResult} = controller;
// to update the post you can call searchPost
// import it from search provider
searchPost(dispatch, posts)

How to use variables across files in React JS

So I have set two datepickers which needs to be reformatted to MM-YYYY which I done with the following code:
const [dateFrom, setDateFrom ] = useState('2012-01-01')
const [dateTill, setDateTill ] = useState('2012-12-31')
const [yearFrom, monthFrom, dayFrom] = dateFrom.split('-');
const newDateFrom = `${monthFrom}-${yearFrom}`
const [yearTill, monthTill, dayTill] = dateTill.split('-');
const newDateTill = `${monthTill}-${yearTill}`
export default {newDateFrom, newDateTill}
I want to use the newDateFrom and newDateTill variables in my api file located in my api folder. I have used string interpolation here.
export const fetchTimeData = async () => {
const response = await fetch(`https://api.punkapi.com/v2/beers?brewed_before=${newDateTill}&brewed_after=${newDateFrom}&per_page=80`)
const data = await response.json();
return data
}
How can I get this to work? I'm not sure if props would work as there isn't a parent-child relationship going on here?
Thank you
You can't call the useState function in the global scope; if you want the variables returned from useState to be accessible to other functions, you need to make a custom hook like the following:
export function useNewDate() {
const [dateFrom, setDateFrom ] = useState('2012-01-01')
const [dateTill, setDateTill ] = useState('2012-12-31')
const [yearFrom, monthFrom, dayFrom] = dateFrom.split('-');
const newDateFrom = `${monthFrom}-${yearFrom}`
const [yearTill, monthTill, dayTill] = dateTill.split('-');
const newDateTill = `${monthTill}-${yearTill}`
return {newDateFrom, newDateTill}
}
Then, because you're using hooks, your fetchTimeData also needs to be a hook:
import { useNewDate } from './useNewDate' // or whichever file your useNewDate hook is stored in
import { useState, useEffect } from 'react';
export const useFetchTimeData = () => {
const { newDateFrom, newDateTill } = useNewDate()
const [timeData, setTimeData] = useState(null)
useEffect(() => {
(async () => {
const response = await fetch(`https://api.punkapi.com/v2/beers?brewed_before=${newDateTill}&brewed_after=${newDateFrom}&per_page=80`)
const data = await response.json();
setTimeData(data)
})();
}, [])
return timeData
}
Then, whenever you want to fetch the time data from within a React component, you would import this hook:
import { useFetchTimeData } from './useFetchTimeData'
export function MyComponent() {
const timeData = useFetchTimeData();
if (timeData === null) {
return <div>Loading time data...</div>
}
return <div>{JSON.stringify(timeData)}</div>
}

How can I manage promise and map objects?

I am working on a project in which data in my database is something like this:
I'm not getting what wrong am I doing! here is my
FetchList.js
import { useState } from "react";
const FetchList = async()=>{
const[data, setData] = useState()
const response = await fetch('https://__firebase__url__');
if (!response.ok) {
throw new Error("Something went wrong");
}
let responseData = await response.json();
const loadedHospitals = [];
for (const key in responseData){
loadedHospitals.push({
key: responseData[key].key,
Name: responseData[key].Name,
Email: responseData[key].Email,
Contact_no: responseData[key].Contact_no,
img: responseData[key].img,
});
}
setData(loadedHospitals);
const hospitals = data.map(hospital => {
console.log(hospital);
});
return data;
}
export default FetchList;
I want to pass the entire result in an array to ListHospital.js and then map it with Card component.
import Card from "../UI/Card";
import FetchList from "./FetchList";
import { useState, useEffect } from "react";
const ListHospitals = () => {
const [data, setData] = useState();
useEffect(() => {
FetchList().then(data => setData(data));
}, []);
return data.map((item)=><Card>{item}</Card>);
}
export default ListHospitals;
Error:
TypeError: Cannot read properties of undefined (reading 'map')
I think you need to write const [data, setData] = useState(); as const [data, setData] = useState([]); first. as fetch list is an async function so it will take some time to fetch your data hence it is giving you an error. You can only use map on arrays and during the initial render the data variable is undefined.
Fetching query at the higher level like App.js solved my problem.
Like :-
function App() {
const [ data, setData ] = useState([]);
const fetching = async () => {
const response = await fetch("_____url___");
const responseData = await response.json();
let loadedItem = [];
for (const key in responseData) {
loadedItem.push(responseData[key]);
}
console.log(loadedItem);
}
fetching();
return (
<>
<h1>Hey</h1>
</>
);
}
export default App;

How to pass array of objects as value in Context Provider

I want to send the array of objects in ContextProvider value attribute, so that i can use it in another component using useContext() hook. So that in the component i can destructure values from the array and map the array and display it on the browser.
Here is the ContextProvider file code:-
import React, { useState, useEffect, createContext } from "react";
import axios from "axios";
const APIContext = createContext();
export const APIContextProvider = (props) => {
const [orders, setOrders] = useState([]);
useEffect(() => {
const fetchData = async () => {
const res = await axios.get("/api/orders");
setOrders(res.data);
//res.data returns ARRAY OF OBJECTS
console.log(res.data);
};
fetchData();
}, []);
return (
<APIContext.Provider value={?}>{props.children}</APIContext.Provider>
);
};
Why you dont pass objects as value={{objects}} and then later access it as const {objects} = useContext(APIContext)

Resources