1

I have a .map() function with JSX code inside. Although, the JSX is not rendering. It is only rendering after I save the file. I am using expo (React Native).

Here is my code:

import React, { useEffect, useState } from "react";
import * as SecureStore from "expo-secure-store";
import { View, Text, ActivityIndicator } from "react-native";
import { Button } from "react-native-elements";

const Receipts = ({ navigation }) => {
  const [receipts, setReceipts] = useState([]);
  const [loading, setLoding] = useState(true);
  const [result, setResult] = useState({});
  const [keys, setKeys] = useState([]);
  useEffect(() => {
    const getReceiptsData = async () => {
      let token = await SecureStore.getItemAsync("token");
      console.log(token);
      fetch("https://notrealapi/api/receipts", {
        method: "GET",
        headers: {
          Authorization: `JWT ${JSON.parse(token)}`,
        },
      })
        .then((res) => res.json())
        .then((json) => {
          setReceipts(json);
          setLoding(false);
        })
        .catch((error) => console.error(error));
    };

    getReceiptsData();
    processReceipts();
  }, []);

  const processReceipts = () => {
    const dubps = [];
    const resultObj = {};
    receipts.map((item) => {
      if (dubps.includes(item.merchant_name)) {
        resultObj[item.merchant_name] =
          resultObj[item.merchant_name] + parseFloat(item.total);
      } else {
        resultObj[item.merchant_name] = parseFloat(item.total);
        dubps.push(item.merchant_name);
      }
    });
    setResult(resultObj);
    setKeys(Object.keys(resultObj));
  };

  const exportReport = async () => {
    let token = await SecureStore.getItemAsync("token");
    fetch("https://notrealapi/api/export", {
      method: "GET",
      headers: {
        Authorization: `JWT ${JSON.parse(token)}`,
      },
    })
      .then((res) => res.json())
      .then((json) => {
        console.log(json);
      })
      .catch((error) => console.error(error));
  };

  const renderSummary = () => {
    return keys.map((key) => {
      return (
        <View>
          <Text
            key={key}
            style={{
              fontSize: 15,
              fontWeight: "normal",
              paddingBottom: 50,
            }}
          >
            {`You have spent $${result[key].toString()} at ${key}`}
          </Text>
        </View>
      );
    });
  };

  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      {loading ? (
        <ActivityIndicator size="large" color="blue" />
      ) : (
        <>
          <Text style={{ fontSize: 30, fontWeight: "bold", paddingBottom: 50 }}>
            Summary:
          </Text>
          {renderSummary()}
          <Button
            type="outline"
            title="Export detailed report"
            onPress={exportReport}
          />
          <Text style={{ fontSize: 10, marginTop: 10 }}>
            *The detailed report shall be sent by email.
          </Text>
        </>
      )}
    </View>
  );
};

export default Receipts;

Note: It does work but only when I save the file and it refreshes using expo CLI. Also, error occurs in the renderSummary() function.

Update: keys can be equal to ["Costco"] and result can be equal to {Costco: 69.99}

4
  • Why you use map if you do not create new array? Commented Jun 21, 2021 at 23:09
  • keys is the array. Commented Jun 21, 2021 at 23:10
  • It's not a good idea to have duplicated state. Also, are you sure you're not running processReceipts() before getReceiptsData() is complete? Commented Jun 21, 2021 at 23:18
  • What do you mean by duplicated state? Also, how would you eliminate the risk of getReceiptsData() running before processReceipts()? Commented Jun 21, 2021 at 23:22

1 Answer 1

1

You are running processReceipts() before the fetch within getReceiptsData() has resolved.

Notice the order of the console logs in this example.

import React, { useEffect, useState } from "react";

const Receipts = () => {
  const [receipts, setReceipts] = useState([]);
  const [loading, setLoding] = useState(true);
  const [result, setResult] = useState({});
  const [keys, setKeys] = useState([]);

  useEffect(() => {
    const getReceiptsData = async () => {
      fetch("https://rickandmortyapi.com/api/character/1", {
        method: "GET"
      })
        .then((res) => res.json())
        .then((json) => {
          console.log("getReceiptsData resolves");
          setReceipts(json);
          setLoding(false);
        })
        .catch((error) => console.error(error));
    };

    getReceiptsData(); // js won't wait here
    processReceipts();
  }, []);

  const processReceipts = (json) => {
    console.log("processReceipts()");
  };
  return null;
};

export default Receipts;

Edit processReceipts-executes-too-early-early


Instead, handle the data manipulation when the fetch resolves.

import React, { useEffect, useState } from "react";

const Receipts = () => {
  const [loading, setLoding] = useState(true);
  const [result, setResult] = useState({});

  useEffect(() => {
    const getReceiptsData = async () => {
      fetch("https://rickandmortyapi.com/api/character/1", {
        method: "GET"
      })
        .then((res) => res.json())
        .then((json) => {
          console.log("getReceiptsData resolves");
          processReceipts(json);
          setLoding(false);
        })
        .catch((error) => console.error(error));
    };

    getReceiptsData();
  }, []);

  const processReceipts = (json) => {
    console.log("processReceipts()");
    // do some work and then setResult
  };
  return null;
};

export default Receipts;

Edit processReceipts-executes-too-early-early (forked)


Also, avoid storing a state that is derived from another state where possible. You should either translate the server payload into usable data:

  • when you receive the payload then set it to state OR
  • when you are rendering
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.