React reports props."function" is not a function
P粉475315142
P粉475315142 2023-09-04 10:38:59
0
1
670
<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粉475315142
P粉475315142

reply all(1)
P粉807471604

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;

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template