3

I'm trying to access to a state object that has a property called allMemes.

After fetching the json data, it will return array of 100 elements.

Then the other property is to use this array to get a random number out of the length. So far I keep getting the error "TypeError: Cannot read properties of undefined (reading 'url')".

I want to understand this error and have some feedback about my code. Thanks!

    const Memes = () => {
        const INITIAL_STATE = {
            topText: '',
            bottomText: '',
            randomImg:'https://i.imgflip.com/26am.jpg',
            allMemes: [],
        };
        
       
      useEffect(() => {
          fetchMemes()
      }, []);
    
      const fetchMemes = () => {
          
      console.log('loading memes data');
      try {
        fetch('https://api.imgflip.com/get_memes')
        .then(response => response.json())
        .then(response => {
         const memesArray = response.data.memes;
         console.log('memeArray', memesArray);
        setInputs(inputs =>({...inputs, allMemes: {...inputs.allMemes.memesArray}}));
        // console.log('inputs', inputs);    
        });        
      } catch (error) {
          console.log(error)
      }       
  }

 

     const handleChange = (e) => {
        const {name, value} = e.target;
        setInputs(inputs => ({...inputs, [name]: value}))    
    };
      const handleSubmit = (e) => {
        e.preventDefault();
        const randNum = Math.floor(Math.random() * inputs.allMemes.length);
        setInputs(inputs =>({...inputs, randomImg: inputs.allMemes[randNum].url}));
        // console.log('randomimg', inputs.randomImg);
        // console.log('memes', inputs.allMemes);
    };
    const [inputs, setInputs] = useState(INITIAL_STATE);
     
    return (
        <>
           <form onSubmit={handleSubmit}>
            <label htmlFor="top">Top Text:</label>
            <input type="text" name="topText" id="top" value={inputs.topText} onChange={handleChange}></input>
            <label htmlFor="btm">Bottom Text:</label>
            <input type="text" name="bottomText" id="btm" value={inputs.bottomText} onChange={handleChange}></input>
            <button>Generate</button>  
           </form>
            <div>
                <h3>{inputs.topText}</h3>
                <h3>{inputs.bottomText}</h3>
                <h3>{inputs.randomImg}</h3>
                <image src={inputs.randomImg} alt='' />
            </div>
     
        </>
    )
}

export default Memes;
Bourbia Brahim
  • 14,459
  • 4
  • 39
  • 52
allaroundcoder
  • 169
  • 1
  • 11

2 Answers2

3

In the fetchMemes() method , you are setting

  1. wrong data type : ( assigning array as and object )

setInputs(inputs =>({...inputs, allMemes: {...inputs.allMemes.memesArray}}));
------------------------------------------^ 

you should set array instead of object [] / {}

    • you didn't passed fetched data , so you got always undefined array :

...memesArray instead of ...inputs.allMemes.memesArray

also change image to img tag

See below working snippet

// Get a hook function
const {
  useState,
  useEffect
} = React;

const Memes = () => {
  const INITIAL_STATE = {
    topText: '',
    bottomText: '',
    randomImg: 'https://i.imgflip.com/26am.jpg',
    allMemes: [],
  };


  useEffect(() => {
    fetchMemes()
  }, []);

  const fetchMemes = () => {

    console.log('loading memes data');
    try {
      fetch('https://api.imgflip.com/get_memes')
        .then(response => response.json())
        .then(response => {
          const memesArray = response.data.memes;
          //console.log('memeArray', memesArray);
          setInputs(inputs => ({ ...inputs,
            allMemes: [ ...memesArray
            ]
          }));
          // console.log('inputs', inputs);    
        });
    } catch (error) {
      console.log(error)
    }
  }

  const handleChange = (e) => {
    const {
      name,
      value
    } = e.target;
    setInputs(inputs => ({ ...inputs,
      [name]: value
    }))
  };
  const handleSubmit = (e) => {
    e.preventDefault();
    
    const randNum = Math.floor(Math.random() * inputs.allMemes.length);
    
    setInputs(inputs => ({ ...inputs,
      randomImg: inputs.allMemes[randNum].url
    }));
    // console.log('randomimg', inputs.randomImg);
    // console.log('memes', inputs.allMemes);
  };
  const [inputs, setInputs] = useState(INITIAL_STATE);

  return ( 
  <React.Fragment>
    <form onSubmit={handleSubmit}>
        <label htmlFor="top">Top Text:</label>
        <input type="text" name="topText" id="top" value={inputs.topText} onChange={handleChange}></input>
        <label htmlFor="btm">Bottom Text:</label>
        <input type="text" name="bottomText" id="btm" value={inputs.bottomText} onChange={handleChange}></input>
        <button>Generate</button>  
       </form>
        <div>
            <h3>{inputs.topText}</h3>
            <h3>{inputs.bottomText}</h3>
            <h3>{inputs.randomImg}</h3>
            <img src={inputs.randomImg} height={120} alt='' />
        </div>
     </React.Fragment>   
  );
};

// Render it
ReactDOM.render(<Memes />,document.getElementById("app"));
const Memes = () => {
    const INITIAL_STATE = {
        topText: '',
        bottomText: '',
        randomImg:'https://i.imgflip.com/26am.jpg',
        allMemes: [],
    };
    
   
  useEffect(() => {
      fetchMemes()
  }, []);

  const fetchMemes = () => {
      
  console.log('loading memes data');
  try {
    fetch('https://api.imgflip.com/get_memes')
    .then(response => response.json())
    .then(response => {
     const memesArray = response.data.memes;
     console.log('memeArray', memesArray);
    setInputs(inputs =>({...inputs, allMemes: {...inputs.allMemes.memesArray}}));
    // console.log('inputs', inputs);    
    });        
  } catch (error) {
      console.log(error)
  }       
}

 const handleChange = (e) => {
    const {name, value} = e.target;
    setInputs(inputs => ({...inputs, [name]: value}))    
};
  const handleSubmit = (e) => {
    e.preventDefault();
    const randNum = Math.floor(Math.random() * inputs.allMemes.length);
    setInputs(inputs =>({...inputs, randomImg: inputs.allMemes[randNum].url}));
    // console.log('randomimg', inputs.randomImg);
    // console.log('memes', inputs.allMemes);
};
const [inputs, setInputs] = useState(INITIAL_STATE);
 
return (
    
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Bourbia Brahim
  • 14,459
  • 4
  • 39
  • 52
1

In the fetchMemes function, when you are setting the Inputs do this,

setInputs(inputs =>({...inputs, allMemes: [...inputs.allMemes.memesArray]}));

You should use spread operator on an array while setting allMemes. But, you are using an object.

Upvote the post if it helps you. Thank you

Nishant Bhosale
  • 251
  • 1
  • 12