1- import React , { useEffect , useContext , useReducer , useState } from 'react' ;
1+ import React , { useEffect , useContext , useReducer , useState , useCallback , useMemo } from 'react' ;
2+ import useSWR from 'swr' ;
23import reducer from './reducer' ;
3- // Context created
4- let API = "https://hn.algolia.com/api/v1/search?" ;
5-
6- const initialState = {
7- isLoading :true ,
8- query :" " ,
9- nbPages :0 ,
10- page :0 ,
11- hits :[ ] ,
12- popularNews :[ ] ,
4+ // Base API endpoint
5+ const API = "https://hn.algolia.com/api/v1/search?" ;
6+
7+ // Initial state
8+ const initialState = {
9+ isLoading : true ,
10+ query : " " ,
11+ nbPages : 0 ,
12+ page : 0 ,
13+ hits : [ ] ,
14+ popularNews : [ ] ,
1315} ;
1416const AppContext = React . createContext ( ) ;
15- // Now the provider function
17+
18+ // Fetcher function for SWR caching
19+ const fetcher = ( url ) => fetch ( url ) . then ( res => res . json ( ) ) ;
20+
1621const AppProvider = ( { children } ) => {
17- const [ state , dispatch ] = useReducer ( reducer , initialState ) ;
22+ const [ state , dispatch ] = useReducer ( reducer , initialState ) ;
1823 const [ showPopularNews , setShowPopularNews ] = useState ( false ) ;
1924 const [ bookMark , setBookMark ] = useState ( [ ] ) ;
20-
21-
22- const fetchApiData = async ( url ) => {
23-
24- dispatch ( { type :"SET_LOADING" } ) ;
25- try {
26- const res = await fetch ( url ) ;
27- const data = await res . json ( ) ;
28- console . log ( data ) ;
29- dispatch ( { type :"GET_STORIES" ,
30- payload :{
31- hits :data . hits ,
32- nbPages :data . nbPages ,
25+
26+ // API fetching with caching
27+ const { data, error } = useSWR ( `${ API } query=${ state . query } &page=${ state . page } ` , fetcher ) ;
28+
29+ // Handle API response
30+ useEffect ( ( ) => {
31+ if ( data ) {
32+ dispatch ( {
33+ type : "GET_STORIES" ,
34+ payload : {
35+ hits : data . hits ,
36+ nbPages : data . nbPages ,
3337 }
34- } )
35-
36- }
37- catch ( error ) {
38- console . log ( error ) ;
39- }
40- } ;
41- const fetchPopularNews = async ( ) => {
42- try {
43- dispatch ( { type : "SET_LOADING" } ) ;
44- const res = await fetch ( ` ${ API } query=technology&tags=story` ) ;
45- const data = await res . json ( ) ;
46-
47- const sortedNews = data . hits
48- . filter ( ( item ) => item . num_comments )
49- . sort ( ( a , b ) => ( b . num_comments || 0 ) - ( a . num_comments || 0 ) ) ;
38+ } ) ;
39+ }
40+ if ( error ) {
41+ console . error ( "Error fetching data:" , error ) ;
42+ }
43+ } , [ data , error ] ) ;
44+
45+ // Fetch popular news separately with SWR
46+ const { data : popularData , error : popularError } = useSWR ( ` ${ API } query=technology&tags=story` , fetcher ) ;
47+
48+ useEffect ( ( ) => {
49+ if ( popularData ) {
50+ const sortedNews = popularData . hits
51+ . filter ( ( item ) => item . num_comments )
52+ . sort ( ( a , b ) => ( b . num_comments || 0 ) - ( a . num_comments || 0 ) )
53+ . slice ( 0 , 7 ) ;
5054
5155 dispatch ( {
5256 type : "GET_POPULAR_NEWS" ,
53- payload : sortedNews . slice ( 0 , 7 ) ,
57+ payload : sortedNews ,
5458 } ) ;
55-
56- } catch ( error ) {
57- console . log ( error ) ;
5859 }
59- } ;
60-
61-
62- const searchFn = ( searchQuery ) => {
63- dispatch ( { type :"SEARCH_QUERY" ,
64- payload :searchQuery ,
65- } ) ;
66- } ;
67- const getNextPage = ( ) => {
68- dispatch ( {
69- type :"NEXT_PAGE" ,
70- } )
71- }
72- const getPrevPage = ( ) => {
73- dispatch ( {
74- type :"PREV_PAGE" ,
75- } )
76- }
60+ } , [ popularData , popularError ] ) ;
61+
62+ // Efficient pagination handlers
63+ const getNextPage = useCallback ( ( ) => {
64+ dispatch ( { type : "NEXT_PAGE" } ) ;
65+ } , [ ] ) ;
7766
78- const addBookMark = ( news ) => {
67+ const getPrevPage = useCallback ( ( ) => {
68+ dispatch ( { type : "PREV_PAGE" } ) ;
69+ } , [ ] ) ;
70+
71+ // Memoized bookmark management
72+ const addBookMark = useCallback ( ( news ) => {
7973 const newBookMark = [ ...bookMark , news ] ;
8074 setBookMark ( newBookMark ) ;
8175 localStorage . setItem ( 'bookmarks' , JSON . stringify ( newBookMark ) ) ;
82- }
76+ } , [ bookMark ] ) ;
8377
84- useEffect ( ( ) => {
85- fetchApiData ( `${ API } query=${ state . query } &{state.page}` ) ;
86- } , [ state . query , state . page ] ) ;
78+ // Remove bookmark function
79+ const removeBookMark = useCallback ( ( news ) => {
80+ const updatedBookmarks = bookMark . filter ( ( item ) => item . objectID !== news . objectID ) ;
81+ setBookMark ( updatedBookmarks ) ;
82+ localStorage . setItem ( 'bookmarks' , JSON . stringify ( updatedBookmarks ) ) ;
83+ } , [ bookMark ] ) ;
8784
85+ // Load bookmarks from localStorage on initial render
8886 useEffect ( ( ) => {
89- fetchPopularNews ( ) ;
87+ const storedData = localStorage . getItem ( 'bookmarks' ) ;
88+ if ( storedData ) {
89+ setBookMark ( JSON . parse ( storedData ) ) ;
90+ }
9091 } , [ ] ) ;
9192
92- useEffect ( ( ) => {
93- let storedData = localStorage . getItem ( 'bookmarks' )
94- if ( storedData ) {
95- setBookMark ( JSON . parse ( storedData ) )
96- }
97- } , [ ] )
93+ // Memoize values for better performance
94+ const value = useMemo ( ( ) => ( {
95+ ...state ,
96+ searchFn : ( query ) => dispatch ( { type : "SEARCH_QUERY" , payload : query } ) ,
97+ getNextPage,
98+ getPrevPage,
99+ showPopularNews,
100+ setShowPopularNews,
101+ addBookMark,
102+ removeBookMark,
103+ bookMark,
104+ setBookMark,
105+ } ) , [ state , showPopularNews , bookMark , addBookMark , removeBookMark ] ) ;
106+
107+ // Display error fallback
108+ if ( error || popularError ) {
109+ return < h1 > Error loading data. Please try again later.</ h1 > ;
110+ }
98111
99-
100112 return (
101- < AppContext . Provider value = { { ... state , searchFn , getNextPage , getPrevPage , showPopularNews , setShowPopularNews , addBookMark , bookMark , setBookMark } } >
113+ < AppContext . Provider value = { value } >
102114 { children }
103115 </ AppContext . Provider >
104116 ) ;
105117} ;
106118
107-
108- const useGlobalContext = ( ) =>
109- {
119+ // Custom hook for accessing context
120+ const useGlobalContext = ( ) => {
110121 return useContext ( AppContext ) ;
111122} ;
112123
113- export { AppContext , AppProvider , useGlobalContext } ;
124+ export { AppContext , AppProvider , useGlobalContext } ;
0 commit comments