Category: context

How to connect redux to cart in react native

Hello People. This article explains how to connect redux to cart in react native. In the products screen, we have a product card that has an add to cart button. Let us how the code looks for this example product card.

Productcard.JS

import React from "react";
import {
  StyleSheet,
  View,
  Dimensions,
  Image,
  Text,
  Button,
} from "react-native";

import { connect } from "react-redux";
import * as actions from "../../Redux/Actions/cartActions";
import Toast from "react-native-toast-message";
import EasyButton from "../../Shared/StyleComponents/EasyButton";

import { hostIP } from "../../assets/common/baseUrl";

var { width } = Dimensions.get("window");
const ProductCard = (props) => {
  const { name, price, image, countInStock } = props;
  console.log("image", `${hostIP}${image}`);
  return (
    <View style={styles.container}>
      <Image
        style={styles.image}
        resizeMode="contain"
        source={{
          uri: image
            ? `${hostIP}${image}`
            : "https://cdn.pixabay.com/photo/2012/04/01/17/29/box-23649_960_720.png",
        }}
      />
      <View style={styles.card}></View>
      <Text style={styles.title}>
        {name.length > 15 ? name.substring(0, 15 - 3) + "..." : name}
      </Text>
      <Text style={styles.price}>₹{price}</Text>
      {countInStock > 0 ? (
        <View style={{ marginBottom: 60 }}>
          <EasyButton
           primary
           medium
            onPress={() => {
              props.addItemToCart(props);
              Toast.show({
                topOffset: 60,
                type: "success",
                text1: `${name} added to card`,
                text2: "GO to your card to compleat order",
              });
            }}
          >
            <Text style={{color:'#FFF',fontWeight:'bold'}}>Add</Text>
          </EasyButton>
        </View>
      ) : (
        <Text style={{ marginTop: 20 }}>Currently Unavailable</Text>
      )}
    </View>
  );
};

const mapDispatchToProps = (dispatch) => {
  return {
    addItemToCart: (product) =>
      dispatch(actions.addToCart({ quantity: 1, product })),
  };
};

const styles = StyleSheet.create({
  container: {
    width: width / 2 - 20,
    height: width / 1.7,
    padding: 10,
    borderRadius: 10,
    marginTop: 55,
    marginBottom: 5,
    marginLeft: 10,
    alignItems: "center",
    elevation: 8,
    backgroundColor: "white",
  },
  image: {
    width: width / 2 - 20 - 10,
    height: width / 2 - 20 - 30,
    backgroundColor: "transparent",
    position: "absolute",
    top: -45,
  },
  card: {
    marginBottom: 10,
    height: width / 2 - 20 - 90,
    backgroundColor: "transparent",
    width: width / 2 - 20 - 10,
  },
  title: {
    fontWeight: "bold",
    fontSize: 14,
    textAlign: "center",
  },
  price: {
    fontSize: 20,
    color: "orange",
    marginTop: 10,
  },
});

export default connect(null, mapDispatchToProps)(ProductCard);

To clarify to you what the code is doing, let us read the above code. We are importing a component called EasyButton. This is just a button with customized styling. import {connect} from ‘react-redux’ connects redux with this component. Redux store is now accessible to this component. This product card is a child component of a parent component by the name ProductList. To clarify to you about this line of code “const { name, price, image, countInStock } = props;”, it is actually destructuring data from its parent component.

The UI part of the component contains image, price, count in stock details of the product. In addition, it has a “Add” button. This button has an onPress method. This onPress triggers props.addItem. After that it triggers the redux action addToCart. You can see this functionality at the mapDispatchToProps section.

How to connect redux to cart in react native

Now let us see how the code of redux looks like.

constants.js

export const ADD_TO_CART = "ADD_TOcARD";
export const REMOVE_FROM_CART = "REMOVE_FROM_CART";
export const CLEAR_CART = "CLEAR_CART";

To clarify to you, these are the constants that we can use as names of buttons used for the cart.

cartActions.js

import { ADD_TO_CART, REMOVE_FROM_CART, CLEAR_CART } from "../constants";

export const addToCart = (payload) => {
  return {
    type: ADD_TO_CART,
    payload,
  };
};

export const removeFromCart = (payload) => {
  return {
    type: REMOVE_FROM_CART,
    payload,
  };
};
export const clearCart = () => {
  return {
    type: CLEAR_CART,
  };
};

These are the redux actions. The flow of redux is like actions, reducers and store. In this case, the redux actions we have here are addToCart, removeFromCart and clearCart. To clarify to you about redux reducers, let us see its code.

cartItem.js

import { ADD_TO_CART, REMOVE_FROM_CART, CLEAR_CART } from "../constants";

const cartItems = (state = [], action) => {
  switch (action.type) {
    case ADD_TO_CART:
      return [...state, action.payload];
    case REMOVE_FROM_CART:
      return state.filter((cartItems) => cartItems !== action.payload);
    case CLEAR_CART:
      return (state = []);
  }
  return state;
};

export default cartItems;

As you can see, redux reducers manage the state of the redux constants. After that we can store state in the redux store by using the following code.

store.js

import { createStore, combineReducers, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";

import cartItems from "./Reducers/cartItem";

const reducers = combineReducers({
 cartItems: cartItems
});

const store = createStore(
  reducers,
  composeWithDevTools(applyMiddleware(thunkMiddleware))
);

export default store;

As you can see, state is stored here in the redux store. To clarify to you more about redux, let us see the code of cart.js where the cart items are displayed.

cart.js

import React, { useContext } from "react";
import { View, Dimensions, StyleSheet, TouchableOpacity } from "react-native";
import { Container, Text, Left, Right } from "native-base";

import { SwipeListView } from "react-native-swipe-list-view";

import Icon from "react-native-vector-icons/FontAwesome";
import EasyButton from "../../Shared/StyleComponents/EasyButton";

import * as actions from "../../Redux/Actions/cartActions";
import { connect } from "react-redux";
import CartItem from "./CartItem";
import AuthGlobal from "../../Context/store/AuthGlobal";

var { height, width } = Dimensions.get("window");

const Cart = (props) => {
  const context = useContext(AuthGlobal);

  var total = 0;
  props.cartItems.forEach((cart) => {
    return (total += cart.product.price);
  });

  



  return (
    <>
      {props.cartItems.length ? (
        <Container>
          {/* <H1 style={{ alignSelf: "center" }}>Cart</H1> */}

          <SwipeListView
            data={props.cartItems}
            renderItem={(data) => <CartItem item={data} />}
            renderHiddenItem={(data) => (
              <View style={styles.hiddenContainer}>
                <TouchableOpacity
                  style={styles.hiddenButton}
                  onPress={() => props.removeFromCart(data.item)}
                >
                  <Icon name="trash" color={"white"} size={30} />
                </TouchableOpacity>
              </View>
            )}
            disableRightSwipe={true}
            previewOpenDelay={3000}
            friction={1000}
            tension={40}
            leftOpenValue={75}
            stopLeftSwipe={75}
            rightOpenValue={-75}
          />
          <View style={styles.bottomContainer}>
            <Left>
              <Text style={styles.price}>₹{total}</Text>
            </Left>
            <Right>
              
              <EasyButton
                medium
                danger
                onPress={() => {
                  props.clearCart();
                }}
              >
                <Text style={{ color: "#FFF", fontWeight: "bold" }}>Clear</Text>
              </EasyButton>
            </Right>
            <Right>
            {context.stateUser.isAuthenticated?(<EasyButton
                medium
                primary
                onPress={() => {
                  props.navigation.navigate("Checkout");
                }}
              >
                <Text style={{ color: "#FFF", fontWeight: "bold" }}>
                  Checkout
                </Text>
              </EasyButton>):<EasyButton
                medium
                secondary
                onPress={() => {
                  props.navigation.navigate("Login");
                }}
              >
                <Text style={{ color: "#FFF", fontWeight: "bold" }}>
                  Login
                </Text>
              </EasyButton>}
              
            </Right>
          </View>
        </Container>
      ) : (
        <Container style={styles.emptyContainer}>
          <Text>Looks like your cart is empty</Text>
          <Text>Add product to your cart to get started</Text>
        </Container>
      )}
    </>
  );
};

const mapStateToProps = (state) => {
  const { cartItems } = state;
  return {
    cartItems: cartItems,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    clearCart: () => dispatch(actions.clearCart()),
    removeFromCart: (item) => dispatch(actions.removeFromCart(item)),
  };
};

const styles = StyleSheet.create({
  emptyContainer: {
    height: height,
    alignItems: "center",
    justifyContent: "center",
  },
  listItem: {
    alignItems: "center",
    justifyContent: "center",
    backgroundColor: "white",
  },
  body: {
    margin: 10,
    alignItems: "center",
    flexDirection: "row",
  },
  bottomContainer: {
    flexDirection: "row",
    position: "absolute",
    bottom: 0,
    left: 0,
    backgroundColor: "white",
    elevation: 20,
  },
  price: {
    fontSize: 18,
    margin: 20,
    color: "red",
  },
  hiddenContainer: {
    flex: 1,
    justifyContent: "flex-end",
    flexDirection: "row",
  },
  hiddenButton: {
    backgroundColor: "red",
    justifyContent: "center",
    alignItems: "flex-end",
    paddingRight: 25,
    height: 70,
    width: width / 1.2,
  },
});

export default connect(mapStateToProps, mapDispatchToProps)(Cart);

To clarify to you, this uses Auth Global context to identify the specific user. It also has a get request to get cart data. UI part of this cart.js displays the cart data on screen.

Hope this article on how to connect redux to cart in react native is useful to you. To know how to create a search bar for your mobile app, please read How to create a search bar in react native.

How to transfer data from context to component

Hello People. This article is about how to transfer data from context to component. As you know react context is very useful transferring data to components. Let us see this using an example code below.

hyderabad.json

{
  "html_attributions": [],
  "next_page_token": "some token",
  "results": [
    {
      "business_status": "OPERATIONAL",
      "geometry": {
        "location": {
          "lat": 17.499838,
          "lng": 78.588024
        },
        "viewport": {
          "northeast": {
            "lat": 17.6552887,
            "lng": 78.6768359
          },
          "southwest": {
            "lat": 17.4186855,
            "lng": 78.46588849999999
          }
        }
      },
      "icon": "https://maps.gstatic.com/mapfiles/place_api/icons/v1/png_71/lodging-71.png",
      "name": "XYZ Company",
      "opening_hours": {
        "open_now": true
      },
      "photos": 
         ["https://i.postimg.cc/ZRghDVkL/Screenshot-1961.png",],
      "place_id": "some place id 1",
      "rating": 5,
      "reference": "",
      "user_ratings_total": 832,
      "vicinity": "Banjara Hills, Hyderabad"
    }
   
  ],
  "status": "OK"
}
How to transfer data from context to component

Let us assume you have the above example data with you. The data contains information about restaurants. We can use that data in a restaurant app. And let us suppose you want to transfer this data to a component. We can do this by passing this data through a context. Before passing this data through a context, we need to transform the data so that it is more user friendly on the front end. We can do that by writing the following code.

restaurants.service

import { mocks } from "./mock";
import camelize from "camelize";

export const restaurantsRequest = (location) => {
  return new Promise((resolve, reject) => {
    const mock = mocks[location];
    if (!mock) {
      reject("not found");
    }
    resolve(mock);
  });
};

export const restaurantsTransform = ({ results = [] }) => {
  const mappedResults = results.map((restaurant) => {
    
    return {
      ...restaurant,
      address: restaurant.vicinity,
      isOpenNow: restaurant.opening_hours && restaurant.opening_hours.open_now,
      isClosedTemporarily: restaurant.business_status === "CLOSED_TEMPORARILY",
      photos: restaurant.photos
    };
  });

  return camelize(mappedResults);
};

We can store the response data coming from google locally. After that we can use this mock data anywhere in the app. Any component that requires it can have access to this data locally. since the data is coming from local storage, we need to mock a promise. After the promise gets accepted, we write a transform function that changes the data format in a more front end friendly manner. After that we can pass this data through context as follows.

restaurants.context

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

import {
  restaurantsRequest,
  restaurantsTransform,
} from "./restaurants.service";

import { LocationContext } from "../location/location.context";

export const RestaurantsContext = createContext();

export const RestaurantsContextProvider = ({ children }) => {
  const [restaurants, setRestaurants] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const { location } = useContext(LocationContext);

  const retrieveRestaurants = (loc) => {
    setIsLoading(true);
    setRestaurants([]);

    restaurantsRequest(loc)
      .then(restaurantsTransform)
      .then((results) => {
        setError(null);
        setIsLoading(false);
        setRestaurants(results);
      })
      .catch((err) => {
        setRestaurants([]);
        setIsLoading(false);
        setError(err);
      });
  };
  useEffect(() => {
    if (location) {
      const locationString = `${location.lat},${location.lng}`;
      retrieveRestaurants(locationString);
    }
  }, [location]);

  return (
    <RestaurantsContext.Provider
      value={{
        restaurants,
        isLoading,
        error,
      }}
    >
      {children}
    </RestaurantsContext.Provider>
  );
};

Now the data is present in the file restaurants.context. This data can be passed on to any component using the context provider. To clarify to you we are passing this data to a component “restaurant.info-card.component”. Let us see the code of this component.

restaurant.info-card.component.js

import React from "react";
import { SvgXml } from "react-native-svg";
import { View } from "react-native";

import { Favourite } from "../../../components/favourites/favourite.component";
import { Spacer } from "../../../components/spacer/spacer.component";
import { Text } from "../../../components/typography/text.component";
import star from "../../../../assets/star";
import open from "../../../../assets/open";

import {
  RestaurantCard,
  RestaurantCardCover,
  Info,
  Section,
  SectionEnd,
  Rating,
  Icon,
  Address,
} from "./restaurant-info-card.styles";

export const RestaurantInfoCard = ({ restaurant = {} }) => {
  const {
    name = "Some Restaurant",
    icon = "https://maps.gstatic.com/mapfiles/place_api/icons/v1/png_71/lodging-71.png",
    photos = [
      "https://www.foodiesfeed.com/wp-content/uploads/2019/06/top-view-for-box-of-2-burgers-home-made-600x899.jpg",
    ],
    address = "100 some random street",
    isOpenNow = true,
    rating = 4,
    isClosedTemporarily = true,
    placeId,
  } = restaurant;

  const ratingArray = Array.from(new Array(Math.floor(rating)));

  return (
    <RestaurantCard elevation={2}>
      <View>
        <Favourite restaurant={restaurant} />
        <RestaurantCardCover key={name} source={{ uri: photos[0] }} />
      </View>
      <Info>
        <Text variant="label">{name}</Text>
        <Section>
          <Rating>
            {ratingArray.map((_, i) => (
              <SvgXml
                key={`star-${placeId}-${i}`}
                xml={star}
                width={20}
                height={20}
              />
            ))}
          </Rating>
          <SectionEnd>
            {isClosedTemporarily && (
              <Text variant="error">CLOSED TEMPORARILY</Text>
            )}
            <Spacer position="left" size="large">
              {isOpenNow && <SvgXml xml={open} width={20} height={20} />}
            </Spacer>
            <Spacer position="left" size="large">
              <Icon source={{ uri: icon }} />
            </Spacer>
          </SectionEnd>
        </Section>
        <Address>{address}</Address>
      </Info>
    </RestaurantCard>
  );
};

As you can see from the code above, the data from context is not passed from context to the above component. But we import context in the parent component and pass this data as props from parent component to its child component. To clarify to you, let us see the code of parent component. It has the name restaurants.screen.

restaurants.screen.js

import React, { useContext, useState } from "react";
import { TouchableOpacity } from "react-native";
import styled from "styled-components/native";
import { ActivityIndicator, Colors } from "react-native-paper";

import { FadeInView } from "../../../components/animations/fade.animation";
import { SafeArea } from "../../../components/utility/safe-area.component";
import { Spacer } from "../../../components/spacer/spacer.component";
import { Text } from "../../../components/typography/text.component";
import { FavouritesBar } from "../../../components/favourites/favourites-bar.component";

import { LocationContext } from "../../../services/location/location.context";
import { RestaurantsContext } from "../../../services/restaurants/restaurants.context";
import { FavouritesContext } from "../../../services/favourites/favourites.context";

import { Search } from "../components/search.component";
import { RestaurantInfoCard } from "../components/restaurant-info-card.component";

import { RestaurantList } from "../components/restaurant-list.styles";

const Loading = styled(ActivityIndicator)`
  margin-left: -25px;
`;
const LoadingContainer = styled.View`
  position: absolute;
  top: 50%;
  left: 50%;
`;

export const RestaurantsScreen = ({ navigation }) => {
  const { error: locationError } = useContext(LocationContext);
  const { isLoading, restaurants, error } = useContext(RestaurantsContext);
  const { favourites } = useContext(FavouritesContext);
  const [isToggled, setIsToggled] = useState(false);
  const hasError = !!error || !!locationError;
  return (
    <SafeArea>
      {isLoading && (
        <LoadingContainer>
          <Loading size={50} animating={true} color={Colors.blue300} />
        </LoadingContainer>
      )}
      <Search
        isFavouritesToggled={isToggled}
        onFavouritesToggle={() => setIsToggled(!isToggled)}
      />
      {isToggled && (
        <FavouritesBar
          favourites={favourites}
          onNavigate={navigation.navigate}
        />
      )}
      {hasError && (
        <Spacer position="left" size="large">
          <Text variant="error">Something went wrong retrieving the data</Text>
        </Spacer>
      )}
      {!hasError && (
        <RestaurantList
          data={restaurants}
          renderItem={({ item }) => {
            return (
              <TouchableOpacity
                onPress={() =>
                  navigation.navigate("RestaurantDetail", {
                    restaurant: item,
                  })
                }
              >
                <Spacer position="bottom" size="large">
                  <FadeInView>
                    <RestaurantInfoCard restaurant={item} />
                  </FadeInView>
                </Spacer>
              </TouchableOpacity>
            );
          }}
          keyExtractor={(item) => item.name}
        />
      )}
    </SafeArea>
  );
};

As you can see in the above code, data is passed as props to the child component. The data already present in the child component becomes dummy now. Context data has the highest priority and it will be rendered the child component.

Hope this article on how to transfer data from context to component is useful to you. To know how to create a search bar for your mobile app, please read How to create a search bar in react native.

How to create location context in expo

Hello People. This article discusses about how to create location context in expo. Location context stores location data and you can import it in any component. Let us analyse this with the help of an example code.

location.context.js

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

import { locationRequest, locationTransform } from "./location.service";

export const LocationContext = React.createContext();

export const LocationContextProvider = ({ children }) => {
  const [keyword, setKeyword] = useState("San Francisco");
  const [location, setLocation] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const onSearch = (searchKeyword) => {
    setIsLoading(true);
    setKeyword(searchKeyword);
  };

  useEffect(() => {
    if (!keyword.length) {
      // don't do anything
      return;
    }
    locationRequest(keyword.toLowerCase())
      .then(locationTransform)
      .then((result) => {
        setIsLoading(false);
        setLocation(result);
      })
      .catch((err) => {
        setIsLoading(false);
        setError(err);
      });
  }, [keyword]);

  return (
    <LocationContext.Provider
      value={{
        isLoading,
        error,
        location,
        search: onSearch,
        keyword,
      }}
    >
      {children}
    </LocationContext.Provider>
  );
};

Let us see what’s happening in this code here. This location context is importing locationRequest and locationTransform from the component location.service. We can see the code of location.service in a while. Coming back to location context, this component is creating a context in this file. State variables in this component are keyword, location, loading and error. We manage their state using useState hook.

onSearch is a function that takes searchKeyword as argument and it sets the setKeyword state variable with searchKeyword. useEffect is the hook that renders first. Here in this case, if no keyword is entered, it does nothing. But when the user enters a keyword, then it will set the setLocation variable with the location results.

How to create location context in expo

We can use this location context as a provider in any component. Now let us see what’s happening in the location.service file.

location.service.js

import camelize from "camelize";

import { locations } from "./location.mock";

export const locationRequest = (searchTerm) => {
  return new Promise((resolve, reject) => {
    const locationMock = locations[searchTerm];
    if (!locationMock) {
      reject("not found");
    }
    resolve(locationMock);
  });
};

export const locationTransform = (result) => {
  const formattedResponse = camelize(result);
  const { geometry = {} } = formattedResponse.results[0];
  const { lat, lng } = geometry.location;

  return { lat, lng, viewport: geometry.viewport };
};

location.service file converts the results into camel casing. It also executes a promise to get location based on the search term. If the searchterm does not contain the location from the data, it rejects the promise. In conclusion latitude, longitude and viewport are returned at the end of execution of this file. We can use this data anywhere in the app by importing it in that specific component. Especially in the home screen of a food delivery app that displays restaurants based on the user’s location, location context is very useful. The user can filter restaurants using the location context and by creating a filter function.

Hope this article on How to create location context in expo is useful to you. Also read How to create mock data in react native expo

Navigation

× Contact Us