Преобразование сайта reactjs в приложение react native

Одним из лучших качеств React является возможность относительно быстро перенести его в приложение react native, поскольку большинство бизнес-логики остается прежней.
Не знаю, стоит ли создавать приложение с нуля, используя react native, но размер пакета может отпугнуть нового пользователя. Если ваше приложение зависит от производительности, то определенно рассмотрите путь kotlin/swift native.
Я бы не рекомендовал его ни для чего, кроме преобразования сайта на реакте в нативное приложение в случае, когда нативное приложение не было в бюджете с самого начала.

Итак, давайте преобразуем этот живой чат react app в react app
репозиторий кода
Собранный apk для android

на react native, это заняло у меня меньше дня, и, вероятно, заняло бы гораздо больше времени, если бы я попробовал сделать это в kotlin.

Я использовал документацию expo

create-expo-app -t expo-template-blank-typescript

cd my-app
Вход в полноэкранный режим Выход из полноэкранного режима

единственное серьезное изменение в версии expo — недоступность локального хранилища, поэтому я использовал @react-native-async-storage/async-storage

App.tsx

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import JoinRoom from './components/JoinRoom';
import { useState } from 'react';
import { getLocalStorageData } from './utils/storage';
import { useEffect } from 'react';
import UserContext from './utils/context';
import { User } from './utils/types';
import Chats from './components/Chats';
import Loading from './components/Loading';
import { useCountdownTimer } from 'use-countdown-timer';

// let the_user:any
// const getUser = async()=>{
//  the_user = await getLocalStorageData()
// }

export default function App() {

 const [user, setUser] = useState<User>({username:"",room:""});
 const updateUser = (new_user:User) => {setUser(new_user)};
 const [loading, setLoading] = useState(true);
 const [timeup, setTimeUp] = useState(true);



useEffect(()=>{
const timeout = setTimeout(() => {
setTimeUp (false);
}, 2000);

getLocalStorageData()
 .then((res)=>{
  const local_user = res as User
   updateUser(local_user)
   if(!countdown){
    setLoading(false)
   }
  })


 return () => { 
  clearTimeout(timeout);
}; 

 },[])


const user_exists = user && user?.username !==""

return (
   <View style={styles.container}>
    <View style={styles.status}>
    <StatusBar style="auto" />
    </View>

      <View style={styles.chats}>
      {loading && timeup ?<Loading />:
       <UserContext.Provider  value ={{user,updateUser}}>
       {user_exists?<Chats/>:<JoinRoom/>}  
       </UserContext.Provider>}
      </View>
      </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'flex-end',
    height:'100%'

  },
  status: {
   alignItems: 'center',
    justifyContent: 'flex-end',
    height:'5%',
    width:'100%',

  },
  chats: {
   backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'flex-end',
    height:'95%',
    width:'100%',
  },

});

Вход в полноэкранный режим Выход из полноэкранного режима

мы также добавим таймаут на 2 секунды, потому что асинхронному хранилищу требуется несколько секунд для проверки локального хранилища.

Хук useChsts полностью идентичен

import { Room, User } from "./types"
import { useRef,useState,useEffect } from 'react';
import socketIOClient,{ Socket } from 'socket.io-client';

const NEW_MESSAGE_ADDAED = "new_message_added";
const ROOM_DATA = "room_data";

const devUrl="http://localhost:4000"
const lanUrl="http://192.168.43.238:4000"
const prodUrl="https://sockets-server-ke.herokuapp.com/"



const useChats=(user:User)=>{

const socketRef = useRef<Socket>();
const [messages, setMessages] = useState<any>([]);
const [room, setRoom] = useState<Room>({users:0,room:""});


useEffect(() => {
socketRef.current = socketIOClient(prodUrl, {
query: { room:user.room,user:user.username },
transports: ["websocket"],
withCredentials: true,
extraHeaders:{"my-custom-header": "abcd"}

})


socketRef.current?.on(NEW_MESSAGE_ADDAED, (msg:any) => {
// //console.log("new message  added==== ",msg)
setMessages((prev: any) => [msg,...prev]);
 });

socketRef.current?.on(ROOM_DATA, (msg:any) => {
//console.log("room data  ==== ",msg)
 setRoom(msg)});

return () => {socketRef.current?.disconnect()};
}, [])

const sendMessage = (message:any) => {
//console.log("sending message ..... === ",message)
 socketRef.current?.emit("new_message", message)
};




return {room,messages,sendMessage}
}
export default useChats

Вход в полноэкранный режим Выход из полноэкранного режима

JoinRoom.tsx

import { StyleSheet,View,Text} from 'react-native'
import React ,{useContext}from 'react'
import { useFormik } from 'formik';
import Button from './CustomButton';
import TextInput from './CustomInput';
import { storeLocalStorageData } from './../utils/storage';
import UserContext from './../utils/context';
import axios from 'axios';
import {LinearGradient} from 'expo-linear-gradient';

import * as yup from 'yup'
import { useState } from 'react';



interface JoinRoomProps{

}
const JoinRoom: React.FC<JoinRoomProps> = () => {


  const devUrl="http://localhost:4000"
  const lanUrl="http://192.168.43.238:4000"
  const prodUrl="https://sockets-server-ke.herokuapp.com/"


  const client = axios.create({ baseURL:prodUrl});
  const user = useContext(UserContext);

const [error, setError] = useState({ name:"", message:"" });
const { handleChange, handleSubmit, values,errors,isSubmitting } = useFormik({

    initialValues: { username:'',room:'general' },

     onSubmit: values =>{
      const roomname = values.room?values.room.toLowerCase():"general"
      const username = values.username.toLowerCase()
      const room_data = {username,room:roomname}

     client.post('/users', {user:room_data})
     .then( (response)=> {
     const user_exist =response.data.data
      console.log("user exists === ",user_exist,room_data)

     if(user_exist){
      console.log("error block")
      setError({name:"username",message:"username exists"})
      errors.username = "username exists"
      }else{
        console.log("no error block")
        storeLocalStorageData(room_data)
        user.updateUser(room_data)  
      }



     })
     .catch(function (error) {

      });

    }

  })
  console.log("errors",errors.username)

  const validationColor = "white"
  const textColor = "white"

return (
    <View 
    style={styles.container}>
     <LinearGradient colors={['#164e63', '#1b9999', '#851ea3']} style={styles.linearGradient}>   
    <View style={styles.formbox}>

   <TextInput onChangeText={handleChange('username')} value={values.username} 
   validationColor={validationColor} textcolor={textColor}/>
   {/* {errors.username &&<Text style={{ fontSize: 15, color: 'yellow' }}>{errors.username}</Text>} */}
   {error.name==="username" &&<Text style={{ fontSize: 15, color: 'yellow' }}>{error.message}</Text>}
   <View style={styles.inputbuffer}></View>

   <TextInput onChangeText={handleChange('room')} value={values.room} 
    validationColor={validationColor} textcolor={textColor}
   />
   {error.name === "room" &&<Text style={{ fontSize: 15, color: 'yellow' }}>{error.message}</Text>}

   <View style={styles.button}>
   <Button onPress={handleSubmit} label="JOIN" color={textColor} />
   </View>

   </View>
   </LinearGradient>
</View>
  )
}

export default JoinRoom

const styles = StyleSheet.create({
  container:{

    flex:1,
    width:'100%',
    height:"100%",
    marginTop:15


   },
 inputbuffer:{
 height:20,
 flexDirection:'column',
 justifyContent:'center',
 alignItems:'center',
 width:'100%'
},
linearGradient: {
  flex: 1,
  width:'100%',
  height:"100%",
  flexDirection:'column',
  justifyContent:'center',
  alignItems:'center',

},
formbox:{
  flex:.5,
  height:100,
  backgroundColor:"#330033",
  flexDirection:'column',
  justifyContent:'center',
  alignItems:'center',
  width:'95%',
  borderRadius:10,
  elevation:7,
  shadowColor:'#00ff33',
  shadowOffset: {
    width: 5,
    height: 25,
  },
  shadowOpacity:  .9,
  shadowRadius: 50.05,
 }
,
button:{
marginTop:20,


 }

})
Вход в полноэкранный режим Выход из полноэкранного режима

единственное большое отличие здесь в том, что мы используем formik, который является опциональным, и вы можете использовать обычную форму, вы также можете использовать formik на сайте, если вы этого хотите

Другой компонент — это компонент линейного градиента для имитации эффекта градиентного цвета на сайте, который был достигнут с помощью нескольких классов tailwindcss
линейный градиент react

И, кажется, есть способ использовать tailwindcss в react native ссылка на статью
Я попробую, но дайте мне знать, если вы меня опередите.

репозиторий кода
Собранный apk для android

репо клиента reactjs
серверное репо

Оцените статью
devanswers.ru
Добавить комментарий