Mastering Mobile Views: Detecting Portrait Mode in React

react-responsive-design

To detect whether your React application is being viewed in portrait mode on a mobile device, you can use a combination of JavaScript’s window.matchMedia() and React hooks. While isPhonePortraint isn’t a built-in React feature, you can easily implement similar functionality. Here are a few approaches you can use:

Option 1: Custom Hook for Portrait Mode Detection

You can create a custom hook to check if the device is in portrait mode:

import { useEffect, useState } from "react";

export function useIsPhonePortrait() {
  const [isPortrait, setIsPortrait] = useState(window.matchMedia("(orientation: portrait)").matches);

  useEffect(() => {
    const mediaQuery = window.matchMedia("(orientation: portrait)");
    const handleChange = () => setIsPortrait(mediaQuery.matches);

    mediaQuery.addEventListener("change", handleChange);
    return () => mediaQuery.removeEventListener("change", handleChange);
  }, []);

  return isPortrait;
}

Usage in a component:

import React from "react";
import { useIsPhonePortrait } from "./useIsPhonePortrait";

export default function MyComponent() {
  const isPortrait = useIsPhonePortrait();

  return (
    <div>
      {isPortrait ? (
        <p>You're in portrait mode 📱</p>
      ) : (
        <p>Landscape mode detected 🌄</p>
      )}
    </div>
  );
}

Option 2: Use react-device-detect Library

Install the package:

npm install react-device-detect

Then use it like this:

import { isMobile } from "react-device-detect";

function App() {
  return (
    <div>
      {isMobile ? <p>Mobile view</p> : <p>Desktop view</p>}
    </div>
  );
}

Note: This library uses user-agent sniffing, which may not detect orientation changes dynamically

Bonus Tip: Combine Mobile + Portrait Detection

To ensure you’re targeting mobile portrait view, combine both checks:

const isMobilePortrait = window.innerWidth < 768 && window.matchMedia("(orientation: portrait)").matches;

Or wrap it in a hook for dynamic updates.

react-responsive-design
react-responsive-design

Option 3: Reusable Portrait Detection Component

You could create a React hook or component that encapsulates the logic for detecting portrait mode. For example:

// usePortraitMode.js
import { useState, useEffect } from 'react';

export function usePortraitMode() {
  const [isPortrait, setIsPortrait] = useState(window.innerHeight > window.innerWidth);

  useEffect(() => {
    const handleResize = () => {
      setIsPortrait(window.innerHeight > window.innerWidth);
    };
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return isPortrait;
}

Then use it like this:

import { usePortraitMode } from './usePortraitMode';

function MyComponent() {
  const isPortrait = usePortraitMode();

  return (
    <div>
      {isPortrait ? "You're in portrait mode!" : "Landscape mode detected."}
    </div>
  );
}

Integrate with Responsive Styles

You can combine orientation detection with CSS-in-JS or utility classes to dynamically style components:

const containerStyle = {
  padding: '20px',
  backgroundColor: isPortrait ? '#f0f8ff' : '#ffe4e1',
  textAlign: 'center',
};

Bridging Backends and Frontends: Spring Boot Meets React

springboot-reactjs

To migrate a Spring Boot project to include a React.js frontend, I’ll walk you through a full-stack setup where Spring Boot serves as the backend API and React handles the frontend. This guide assumes you’re starting with a Spring Boot backend and want to integrate or migrate to a React-based frontend. This setup includes:

  • A simple Spring Boot backend with one API endpoint.
  • A React frontend that fetches data from the backend.
  • Instructions to run both parts and serve React from Spring Boot in production.

Project Structure Overview:

spring-react-app/
├── backend/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/com/example/demo/
│   │   │   │   ├── DemoApplication.java
│   │   │   │   └── HelloController.java
│   │   │   └── resources/
│   │   │       └── application.properties
│   └── pom.xml
├── frontend/
│   ├── public/
│   ├── src/
│   │   └── App.js
│   └── package.json

Migration Steps:

    1. Spring Boot Backend Setup

    Create a Spring Boot project using Spring Initializr with dependencies:

    • Spring Web
    • Spring Boot DevTools
    • Spring Data JPA (if using a database)
    • Spring Security (optional)

    Example pom.xml snippet:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>3.5.4</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    	<groupId>com.softifyo.beautify</groupId>
    	<artifactId>ReactSpringBootApplication</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>ReactSpringBootApplication</name>
    	<description>React JS + Spring Boot Demo</description>
    	<url/>
    	<licenses>
    		<license/>
    	</licenses>
    	<developers>
    		<developer/>
    	</developers>
    	<scm>
    		<connection/>
    		<developerConnection/>
    		<tag/>
    		<url/>
    	</scm>
    	<properties>
    		<java.version>21</java.version>
    		<spring-ai.version>1.0.0</spring-ai.version>
    	</properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-devtools</artifactId>
    			<scope>runtime</scope>
    			<optional>true</optional>
    		</dependency>
    		<dependency>
    			<groupId>org.projectlombok</groupId>
    			<artifactId>lombok</artifactId>
    			<optional>true</optional>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.restdocs</groupId>
    			<artifactId>spring-restdocs-mockmvc</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.maven.plugins</groupId>
    				<artifactId>maven-compiler-plugin</artifactId>
    				<configuration>
    					<annotationProcessorPaths>
    						<path>
    							<groupId>org.projectlombok</groupId>
    							<artifactId>lombok</artifactId>
    						</path>
    					</annotationProcessorPaths>
    				</configuration>
    			</plugin>
    			<plugin>
    				<groupId>org.asciidoctor</groupId>
    				<artifactId>asciidoctor-maven-plugin</artifactId>
    				<version>2.2.1</version>
    				<executions>
    					<execution>
    						<id>generate-docs</id>
    						<phase>prepare-package</phase>
    						<goals>
    							<goal>process-asciidoc</goal>
    						</goals>
    						<configuration>
    							<backend>html</backend>
    							<doctype>book</doctype>
    						</configuration>
    					</execution>
    				</executions>
    				<dependencies>
    					<dependency>
    						<groupId>org.springframework.restdocs</groupId>
    						<artifactId>spring-restdocs-asciidoctor</artifactId>
    						<version>${spring-restdocs.version}</version>
    					</dependency>
    				</dependencies>
    			</plugin>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    				<configuration>
    					<excludes>
    						<exclude>
    							<groupId>org.projectlombok</groupId>
    							<artifactId>lombok</artifactId>
    						</exclude>
    					</excludes>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    
    </project>
    

    Create a REST Controller

    @RestController
    @RequestMapping("/api")
    public class HelloController {
        @GetMapping("/hello")
        public ResponseEntity<String> sayHello() {
            return ResponseEntity.ok("Hello from Spring Boot!");
        }
    }
    

    2. Create React Frontend

    Run the below commands inside the project root folder

    npx create-react-app frontend
    cd frontend
    npm start
    

    Replace App.js with a simple fetch call:

    import React, { useEffect, useState } from 'react';
    
    function App() {
      const [message, setMessage] = useState('');
    
      useEffect(() => {
        fetch('/api/hello')
          .then(res => res.text())
          .then(data => setMessage(data));
      }, []);
    
      return <h1>{message}</h1>;
    }
    
    export default App;
    

    3. Proxy API Requests in Development

    Add the below proxy configuration in frontend/package.json file. This allows React to forward /api requests to Spring Boot during development.

    "proxy": "http://localhost:8080"
    

    4. Build React for Production

    npm run build
    

    This generates a static build in frontend/build

    5. Serve React from Spring Boot

    Copy the React build into Spring Boot’s resources/static folder:

    cp -r frontend/build/* backend/src/main/resources/static/
    

    Now Spring Boot will serve the React app at /, and API endpoints at /api.

    Note: Make sure you are located at the project root directory while running the above command otherwise path needs to update accordingly.

    6. Optional: Automate Build Integration

    You can automate React build during Maven build using the frontend-maven-plugin:

    <plugin>
      <groupId>com.github.eirslett</groupId>
      <artifactId>frontend-maven-plugin</artifactId>
      <version>1.12.0</version>
      <executions>
        <execution>
          <id>install node and npm</id>
          <goals><goal>install-node-and-npm</goal></goals>
          <configuration>
            <nodeVersion>v18.0.0</nodeVersion>
            <npmVersion>9.0.0</npmVersion>
          </configuration>
        </execution>
        <execution>
          <id>npm install</id>
          <goals><goal>npm</goal></goals>
          <configuration><arguments>install</arguments></configuration>
        </execution>
        <execution>
          <id>npm run build</id>
          <goals><goal>npm</goal></goals>
          <configuration><arguments>run build</arguments></configuration>
        </execution>
      </executions>
    </plugin>
    

    Additional Points:

    • Use axios or fetch in React to call Spring Boot APIs.
    • Secure your API endpoints with Spring Security if needed.
    • Consider using environment variables for API URLs in React (REACT_APP_API_URL).
    • For advanced setups, consider using Docker or CI/CD pipelines.

    Sample project with similar requirement is available in my GITHUB account.

    Batch Scheduler Example: Spring Boot + WebFlux + R2DBC + Teradata

    spring boot + Web Flux + Teradata + Scheduler

    Creating an end-to-end Spring Boot WebFlux batch API to read data from Teradata involves several steps. I’ll provide a comprehensive example that includes:

    • Spring Boot application setup with WebFlux
    • Reactive repository to fetch data from Teradata
    • Controller to expose the API
    • Basic configuration for connecting to Teradata

    Since WebFlux is reactive, you should use a reactive driver for Teradata. However, Teradata’s official Reactive driver support is limited. But for the purpose of this example, I will demonstrate how you might set up a reactive API assuming the availability of a reactive driver or by wrapping blocking calls in a reactive manner with Mono.fromCallable() or similar.

    Step 1: Dependencies (Spring Boot + WebFlux + R2DBC + Teradata)

    Include the following dependencies in your pom.xml. Note that Teradata does not officially support R2DBC, but you might need a driver or fallback to using JdbcTemplate with Mono.fromCallable() for blocking calls.

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <!-- If you have a reactive Teradata driver, include it here, for example: -->
        <!-- dependency for hypothetical Teradata R2DBC driver -->
        <!-- <dependency>
            <groupId>com.teradata</groupId>
            <artifactId>teradata-r2dbc</artifactId>
            <version>1.0.0</version>
        </dependency> -->
        <!-- Alternatively, fallback with R2DBC Postgres or H2 for testing -->
        <dependency>
            <groupId>io.r2dbc</groupId>
            <artifactId>r2dbc-spi</artifactId>
            <version>0.8.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-r2dbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    

    Step 2: Application Configuration

    Create application properties for Teradata connection (adjust as per your environment):

    # src/main/resources/application.properties
    spring.r2dbc.url=r2dbc:teradata://host:port/database
    spring.r2dbc.username=your_username
    spring.r2dbc.password=your_password
    

    Note: Replace the URL with the correct Teradata R2DBC URL when available.

    spring boot + Web Flux + Teradata + Scheduler

    Step 3: Data Model

    Create a simple data class representing the data:

    package com.example.teradataspring.model;
    
    public class DataRecord {
        private Long id;
        private String data;
    
        // Constructors
        public DataRecord() {}
        public DataRecord(Long id, String data) {
            this.id = id;
            this.data = data;
        }
    
        // Getters and Setters
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getData() {
            return data;
        }
    
        public void setData(String data) {
            this.data = data;
        }
    }
    

    Step 4: Repository Layer

    Since there’s no native reactive driver, here’s an example wrapping blocking JDBC calls with Mono.fromCallable(). Alternatively, if using a reactive driver, you would implement accordingly.

    package com.example.teradataspring.repository;
    
    import com.example.teradataspring.model.DataRecord;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.r2dbc.core.DatabaseClient;
    import reactor.core.publisher.Flux;
    
    public class BatchRepository {
    
        private final DatabaseClient databaseClient;
    
        @Autowired
        public BatchRepository(DatabaseClient databaseClient) {
            this.databaseClient = databaseClient;
        }
    
        public Flux<DataRecord> fetchData() {
            String query = "SELECT id, data FROM your_table"; // your table name
            return databaseClient.sql(query)
                    .map((row, rowMetadata) -> new DataRecord(
                            row.get("id", Long.class),
                            row.get("data", String.class)
                    ))
                    .all();
        }
    }
    

    Note: You need to register BatchRepository as a bean or annotate with @Repository.

    Step 5: Service Layer

    package com.example.teradataspring.service;
    
    import com.example.teradataspring.model.DataRecord;
    import com.example.teradataspring.repository.BatchRepository;
    import org.springframework.stereotype.Service;
    import reactor.core.publisher.Flux;
    
    @Service
    public class DataService {
    
        private final BatchRepository batchRepository;
    
        public DataService(BatchRepository batchRepository) {
            this.batchRepository = batchRepository;
        }
    
        public Flux<DataRecord> getAllData() {
            return batchRepository.fetchData();
        }
    }
    

    Step 6: Controller for regular applications

    package com.example.teradataspring.controller;
    
    import com.example.teradataspring.model.DataRecord;
    import com.example.teradataspring.service.DataService;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import reactor.core.publisher.Flux;
    
    @RestController
    public class DataController {
    
        private final DataService dataService;
    
        public DataController(DataService dataService) {
            this.dataService = dataService;
        }
    
        @GetMapping("/api/data")
        public Flux<DataRecord> getData() {
            return dataService.getAllData();
        }
    }
    

    Step 7: Scheduler (Batch job with @Scheduled)

    // BatchScheduler.java
    package com.example.demo.scheduler;
    
    import com.example.demo.model.DataRecord;
    import com.example.demo.service.DataService;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    import reactor.core.publisher.Flux;
    
    @Component
    public class BatchScheduler {
    
        private final DataService dataService;
    
        public BatchScheduler(DataService dataService) {
            this.dataService= dataService;
        }
    
        @Scheduled(fixedDelay = 60000) // runs every 60 seconds
        public void runBatchJob() {
            dataService.getAllData()
                       .doOnNext(record -> {
                           // process each record, e.g., log or store
                           System.out.println("Processing record: " + record.getId() + ", data: " + record.getData());
                       })
                       .subscribe(); // trigger the reactive flow
        }
    }
    

    Step 8: Main Application Class for normal application

    package com.example.teradataspring;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.r2dbc.core.DatabaseClient;
    import com.example.teradataspring.repository.BatchRepository;
    
    @SpringBootApplication
    public class TeradataSpringBootApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(TeradataSpringBootApplication.class, args);
        }
    
        @Bean
        public BatchRepository batchRepository(DatabaseClient databaseClient) {
            return new BatchRepository(databaseClient);
        }
    }
    

    Step 9: Main Application Class for Scheduler application

    // DemoApplication.java
    package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    @SpringBootApplication
    @EnableScheduling
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }
    

    This setup creates a reactive Spring Boot API using WebFlux to read data from Teradata. It leverages DatabaseClient for reactive database access, which is part of Spring Data R2DBC. If you don’t have a reactive driver for Teradata, you’ll need to adapt the code to wrap blocking calls appropriately.