Search code examples
javascriptreactjsreact-hooksuse-statefluentui-react

can I await for a state before parsing the data in react?


I have a custom hook that gets file data from the cache api based on an url. The hook works fine.

Problem is that I get a response back and I need to parse this data before I can use the fabric tag picker. and I also only want to parse that data when the user starts typing. I know that the prop "filterSuggestedTags" accepts a promise like call and we can fetch data before there.

You will see that I am using a fetch inside the filterSuggestedTags and that ensures to fetch the data from the url before showing the results, plus it shows the loader as well: https://codepen.io/deleite/pen/MWjBMjY?editors=1111

So given the code below and the fact that my useCache gives my a fetch response as state:

const filterSuggestedTags = async (filterText: string, tagList: ITag[]) => {
    if (filterText) {
      // how can I await the response state? before I can parse the data 
      return (await /* resp should be the state from the custom hook*/ resp.json()).map(item => ({ key: item.id, name: item.title }));
    } else return []
  };

Solution

  • Be warned, this is not tested, nor am I of the opinion this the best thing to do, just some thoughts.

    EDIT: After some thinking, this might be better

    This might save you some headaches with old sate, updates.

    const useFilterSuggestedTags = async (filterText: string, tagList: ITag[]) => {
        const [data, setData] = useState<WhateverYouReturn[]>([]);
    
        useEffect(() => {
            const controller = new AbortController();
    
            if (filterText) fetchData();
            
            async function fetchData() {
                try {
                    const resp = await fetch('url', {signal: controller.signal});
                    const json = await resp.json();
    
                    if (!controller.signal.aborted) {
                        setData(json.map(_ => ({ key: _.id, name: _.title, })));
                    }
                } catch(e) {
                    if (!(e instanceof AbortError)) throw e
                }
            }
    
            //  If your effect returns a function, React will run it when it is time to
            // clean up:
            return controller.abort;
        }, [filterText, tagList])
    
        return data;
    };
    

    Old Answer

    How about

    const useFilterSuggestedTags = async (filterText: string, tagList: ITag[]) => {
        const [data, setData] = useState<WhateverYouReturn[]>([]);
    
        if (filterText) fetchData();
    
        return data;
    
        async function fetchData() {
            const resp = await {}; // Obviously fetch something
            const json = await resp.json();
    
            // I assume you also need to filter them, but I guess you know how
            // to do that :P
    
            setData(json.map(_ => ({ key: _.id, name: _.title, })));
        }
    };
    

    Little notice: It is good practice to name your hooks use[...]. At least that is what they (and the react-hooks es-lint plugin) suggest.