search

Home  >  Q&A  >  body text

React reports props."function" is not a function

<p>I'm creating my first online store and I'm running into a problem. I made fake API which contains product card data and when I get them using <code>axios.get</code> and add to state I get "props.addCardData is not a function".I must say, everything works fine until I add <code>axios.get</code> . Other functions I use the same way as the function <code>addCardData</code>: I mean, I used <code>mapDispatchToProps</code> and I used <code>props.addCardData</code> ; - I'm not using them in <code>axios.get</code> or other requests). I had no problem using any of the features before. </p> <p>I plan to get the data via <code>axios.get</code> and call a function from the card container that will call the dispatch with the action creator as a parameter. </p> <p>I also found "Cannot read property of undefined (read 'addCardData').</p> <p>Below, I've indicated the portion of code affected by the issue (I've indicated which code applies to which elements)</p> <p>This is the code in the Cards component (I didn't add the imports here, but ofc I have all the imports): </p> <pre class="brush:php;toolbar:false;">const Cards = (props) => { axios.get('https://mocki.io/v1/8cb0f160-92f7-4cf8-a6c1-f63690df514e') .then(response => { props.addCardData(response.data) }) let cardsArray = Object.keys(props.cardsData).map(card => ( <OfferCard key={card.id} bg={props.cardsData[card].bg} id={props.cardsData[card].tagId} title={props.cardsData[card].title} text={props.cardsData[card].text} button={ <Container fluid> <Row className={'row-cols-auto'}> {props.cardsData[card].button.map(button => ( <CardsButton key={button.id} link={button.link} type={button.type} class={button.class} name={button.name} /> ))} </Row> </Container> } /> )) return ( <Container fluid> <img src={'./backgrounds/bestoffers.png'} alt={'BEST OFFERS'} className={'img-fluid imgTab'} /> <Row xs={1} md={2} id={'cards-row'} className={'border border-4 g-3'}> {/*row-cols-* - set the cards width by setting amount of cards in row*/} {cardsArray} </Row> </Container> ) } export default Cards</pre> <p>This is the code in <code>CardsContainer</code>: </p> <pre class="lang-js prettyprint-override"><code>const mapStateToProps = (state) => { return { cardsData: state.homePage.cardsData } } const mapDispatchToProps = (dispatch) => { return { addCardData: (data) => { dispatch(addCardsData(data)) } } } const CardsContainer = connect(mapStateToProps, mapDispatchToProps)(Cards); export default CardsContainer </code></pre> <p>This is the code in the reducer: </p> <pre class="lang-js prettyprint-override"><code>... homePageCardsData = 'HOMEPAGE-CARDS-DATA' initialState = {...} - includes "cardsData: {}" const homePageReducer = (state = initialState, action) => { let stateCopy; switch (action.type) { case homePageCardsData: { stateCopy = {...state, cardsData: action.data} break } ... default: return state; } return stateCopy; ...... - some functions here (not necessary to know) } export const addCardsData = (data) => ({ type: homePageCardsData, data: data }) </code></pre> <p>When I try something like: </p> <pre class="lang-js prettyprint-override"><code>const addCard = props.addCardData axios.get('https://mocki.io/v1/8cb0f160-92f7-4cf8-a6c1-f63690df514e') .then(response => { addCard(response.data) }) </code></pre> <p>I'm getting lag on localhost (on the homepage!!), everything starts rendering slowly, and 70% of the time, the card block doesn't render. In other cases it can render after some time (rare chance). On the AdminPanel page, I'm rendering <code>Cards</code> because I need to see the changes while testing the menu, I get "TypeError: addCard is not a function."</p> <p>If I remove this code from the component - everything works fine. </p> <p>I must also say that I used the "debugger" and placed it in <code>homePageCardsData</code>. The script stops on the debugger (after <code>StateCopy</code> and before <code>break</code>). This means the script is correct, <code>dispatch</code> works and I can get into the case <code>homePageCardsData</code>. </p>
P粉475315142P粉475315142436 days ago638

reply all(1)I'll reply

  • P粉807471604

    P粉8074716042023-09-05 09:01:34

    The problem is in the Cards component, which makes an Axios GET request as an unintentional side effect directly in the component's function body. This will most likely create a render loop, or at least make a GET request every time Cards is rendered.

    Move this code into the useEffect hook so that it is called after the component is installed.

    Example:

    const Cards = ({ addCardsData, cardsData }) => {
      useEffect(() => {
        axios
          .get("https://mocki.io/v1/8cb0f160-92f7-4cf8-a6c1-f63690df514e")
          .then((response) => {
            addCardsData(response.data);
          });
      }, [addCardsData]);
    
      let cardsArray = Object.keys(cardsData).map((card) => (
        <OfferCard
          key={card.id}
          bg={cardsData[card].bg}
          id={cardsData[card].tagId}
          title={cardsData[card].title}
          text={cardsData[card].text}
          button={
            <Container fluid>
              <Row className={"row-cols-auto"}>
                {cardsData[card].button.map((button) => (
                  <CardsButton
                    key={button.id}
                    link={button.link}
                    type={button.type}
                    class={button.class}
                    name={button.name}
                  />
                ))}
              </Row>
            </Container>
          }
        />
      ));
    
      return (
        ...
      );
    };
    
    const mapStateToProps = (state) => ({
      cardsData: state.homePage.cardsData
    });
    
    const mapDispatchToProps = {
      addCardsData
    };
    
    const CardsContainer = connect(mapStateToProps, mapDispatchToProps)(Cards);

    You are using fairly old Redux code and we generally no longer use the connect higher-order component since the useDispatch and useSelector hooks replaced it usage. The current standard is to use the Redux-Toolkit, which does reduce the amount of boilerplate you need to write.

    The following are examples of update suggestions:

    import { useDispatch, useSelector } from "react-redux";
    ...
    
    const Cards = () => {
      const dispatch = useDispatch();
      const cardsData = useSelector(state => state.homePage.cardsData);
    
      useEffect(() => {
        axios
          .get("https://mocki.io/v1/8cb0f160-92f7-4cf8-a6c1-f63690df514e")
          .then((response) => {
            dispatch(addCardsData(response.data));
          });
      }, [dispatch]);
    
      let cardsArray = Object.keys(cardsData).map((card) => (
        <OfferCard
          key={card.id}
          bg={cardsData[card].bg}
          id={cardsData[card].tagId}
          title={cardsData[card].title}
          text={cardsData[card].text}
          button={
            <Container fluid>
              <Row className={"row-cols-auto"}>
                {cardsData[card].button.map((button) => (
                  <CardsButton
                    key={button.id}
                    link={button.link}
                    type={button.type}
                    class={button.class}
                    name={button.name}
                  />
                ))}
              </Row>
            </Container>
          }
        />
      ));
    
      return (
        ...
      );
    };

    reducer code, operation type, operation creator... all replaced by a single state slice.

    import { createSlice } from "@reduxjs/toolkit";
    
    const initialState = {
      cardsData: {},
      ... other home page state ...
    };
    
    const homePageSlice = createSlice({
      name: "homePage",
      initialState,
      reducers: {
        addCardsData: (state, action) => {
          state.cardsData = action.payload;
        },
        ... other reducer cases ...
      }
    });
    
    export const {
      addCardsData,
      ... other generated actions ...
    } = homePageSlice.actions;
    
    export default homePageSlice.reducer;
    

    reply
    0
  • Cancelreply