Kyounghwan01 / blog

꾸준히 기록하는 개발 블로그!
https://kyounghwan01.github.io/blog/
MIT License
306 stars 9 forks source link

react-native #40

Open Kyounghwan01 opened 2 years ago

Kyounghwan01 commented 2 years ago
Kyounghwan01 commented 2 years ago

시멘틱 ui

Seo 잘되게하는법

Kyounghwan01 commented 2 years ago

nextjs는 dotenv가 다르네

next.config.js

/* eslint-disable @typescript-eslint/no-var-requires */
const Dotenv = require("dotenv-webpack");
const withTM = require('next-transpile-modules')([
  '@mui/material',
  '@mui/system',
]);

module.exports = withTM({
  reactStrictMode: true,
  webpack: (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      '@mui/styled-engine': '@mui/styled-engine-sc',
    };
    config.plugins.push(new Dotenv({ silent: true }));
    return config;
  },
});

추가완료

Kyounghwan01 commented 2 years ago

리눅스에서 nginx

Kyounghwan01 commented 2 years ago

vscode 탭 workbench.editor.showTabs

Kyounghwan01 commented 2 years ago

Failed to execute 'scroll' on 'Window': No function was found that matched the signature provided.

chrome 버전에 따라서 smooth 가 없을 수 있음

Kyounghwan01 commented 2 years ago

nextjs dynamic import with params (typescript)

interface IPointComponent { next: (path: number | null) => void; back: () => void; }

const PointComponents = dynamic(() => import(@domains/point/components/${components[pointStore.currentIndex].component}));


추가완료

Kyounghwan01 commented 2 years ago

express에서 웹 도메인가져오기

Kyounghwan01 commented 2 years ago

setInterval interface type

let interval: ReturnType<typeof setInterval>;
    useEffect(() => {

        return () => {
            clearInterval(interval);
        };
    }, []);
Kyounghwan01 commented 2 years ago

prettier cli

prettier --config ./.prettierrc --write -u
Kyounghwan01 commented 2 years ago

parser 가 제대로 안들어가는 경우는 plugins, extends가 안들어갔을 확률이 높다

Kyounghwan01 commented 2 years ago

typescript 에서 절대경로로 component import 할때

eslint[import/no-unresolved](https://github.com/import-js/eslint-plugin-import/blob/v2.26.0/docs/rules/no-unresolved.md)

이런 에러 나면

yarn add -D eslint-import-resolver-typescript

eslint 설정에서

"settings":{
    "import/resolver":{
       ...
      "typescript":{
        "project": "renderer/tsconfig.json" // tsconfig.json이 root이면 필요없음
      }
    }
  }
Kyounghwan01 commented 2 years ago

husky

  1. 허스키 설치

    yarn add -D husky
  2. 허스키 세팅

    npx husky-init && yarn
  3. lint-staged 설정

    • pre-commit -> yarn lint로 수정
    • package.json
      "lint-staged": {
      "renderer/**/*.{js,ts,jsx,tsx}": [
      "eslint --ext .tsx,.ts . --quiet --fix",
      "prettier --config ./renderer/.prettierrc.json --write -u"
      ]
      }

pre-commit ignore error 시

hint: The 'pre-commit' hook was ignored because it's not set as executable. 아래 실행 (pre-commit 에 맞는 디렉토리로) `chmod +x .husky/pre-commit

typescript 파일이 eslint 에 걸리지 않으면

  "lint-staged": {
    "renderer/**/*.{js,ts,jsx,tsx}": [
      "eslint --ext .tsx,.ts . --quiet --fix",
      "prettier --config ./renderer/.prettierrc.json --write -u"
    ]
  }

완료

Kyounghwan01 commented 2 years ago

useEffect, dependencise에 객체 넣었을 때 객체 내부의 값이 변경되어도 useEffect 호출되지 않음, preimitve 한 값과 연관이 있어보임

Kyounghwan01 commented 2 years ago

dynamic이면 자기자신을 한번더 호출하는거같은데

Kyounghwan01 commented 2 years ago

useEffect, dependencise에 객체 넣었을 때 객체 내부의 값이 변경되어도 useEffect 호출되지 않음, preimitve 한 값과 연관이 있어보임

객체는 객체의 주소값이 바뀌지 않는 이상 useEffect가 실행되지 않음. object, array가 변경시 실행되게 하려면 https://github.com/kentcdodds/use-deep-compare-effect 이거 참조

비슷한 예시로 useState가 있고 그 state가 객체 또는 배열일 때 useEffect에서 그 객체 또는 배열을 참조한다면 해당 컴포넌트가 리렌더링 될때마다 그 state도 리렌더링에 의해 다시 만들어질테고 그렇게된다면 주소값이 바뀌게 되어 useEffect도 재실행된다.

Kyounghwan01 commented 2 years ago

일일 (아침, 저녁), 주간 일기

계정

메인

만들기

아침일기

저녁 일기

주간 회고

달 목표

개별 get

크롬익스텐션

Kyounghwan01 commented 2 years ago

react checkbox 사용 에러

next-dev.js?36dd:24 Warning: You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.

input type으로 checkbox를 쓸 때 onClick 핸들러를 제공하고 checked 값을 설정하는 식으로 코딩하면 이런 에러 메시지가 발생한다.

해결방법 onClick 핸들러를 없애고 onChange 핸들러를 사용한다. onClick 핸들러를 그대로 두고 싶으면 readonly 키워드를 붙이거나 checked 속성 대신 defaultChecked를 사용한다.

Kyounghwan01 commented 2 years ago

turborepo

Kyounghwan01 commented 2 years ago

반영완료

typescript empty object Record<string, never>

Kyounghwan01 commented 2 years ago

반영완료

밑줄 위로 올리기

            text-decoration: underline;
            text-decoration-color: #f0cdb9;
            text-decoration-thickness: 10px;
            text-underline-offset: -10px;
Kyounghwan01 commented 2 years ago

Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".


chrome extenstion eval error

Kyounghwan01 commented 2 years ago

react에서 debounce사용하기 rerendering때문에 debounce가 제대로 작동하지 않는 이유랑 같이 서술 -> useCallback을 사용하는데 dependence를 잘 이용해야함


const changePrice = (e) => {
        const { name, value } = e.currentTarget || e.target;
            const params = {price: value}; handleCarPrice(params);
}

    const handleCarPrice = useCallback(
        debounce(async params => {
            try {
                const response = await api.어떤거{ params });
                setRealCarPrice(Math.floor(response.data.payload.price / 10000));
            } catch (e) {
                openSystemModal({ msg: '값이 없습니다' });
            }
        }, 300),
        [],
    );

반영완료

Kyounghwan01 commented 1 year ago

index.js?4755:251 Uncaught Error: [MobX] Cannot apply '@observable' to 'CarRewardStore.prototype.pagValdYn': The field is already decorated with '@observable'. Re-decorating fields is not allowed. Use '@override' decorator for methods overriden by subclass.

-- store에 같은 변수 두번 이상 선언한 경우

Kyounghwan01 commented 1 year ago

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-MutationObserver-DOM%EC%9D%98-%EB%B3%80%ED%99%94%EB%A5%BC-%EA%B0%90%EC%8B%9C

new mutationObserver

Kyounghwan01 commented 1 year ago

array 같은 값 추가 new Array(3).fill('adawd')

[ "adawd", "adawd", "adawd" ]


완료

Kyounghwan01 commented 1 year ago

TS2604: JSX element type 'Component' does not have any construct or call signatures.

  • component: React.ComponentClass<{ selectIndex: number }>;

Kyounghwan01 commented 1 year ago

이미지 본래 height, width 값 알아내기

    const checkImageWithHeight = ({ target: img }) => {
        // 8개 사진 나오는 미리보기 때 찍었는데 이미지가 세로가 가로보다 크면 이미지 90도 로테이트
        console.log(img.naturalHeight, img.naturalWidth);
    };

<img alt="preview" onLoad={checkImageWithHeight} src={}  />

완료

Kyounghwan01 commented 1 year ago

empty object: {} -> Record<string, never>


완료

Kyounghwan01 commented 1 year ago

Error: A form label must be associated with a control.

-> label에 htmlFor를 input의 id와 일치시킨다

id값은 html input태그에서 label태그와 연결할때 사용한다

label 태그는 checkbox, radio 를 설명하는 문자를 삽일할때 사용하는 태그다.

for 라는 속성을 삽입하여 input의 id와 일치시키면 체크받스 클릭 범위를 증가시킬수있다.

<label for="wow">이그젬플</label>
<input type="checkbox" id="wow">

이렇게 하면 글자를 클릭해도 체크가 된다.

Kyounghwan01 commented 1 year ago

const reader = new FileReader();

reader.onload 가 작동하지 않을때

readAsArrayBuffer, readAsBinaryString, readAsDataURL 또는 readAsText 중 하나를 호출해야 onLoad 작동한다

Kyounghwan01 commented 1 year ago

nextjs redirect -> 특정페이지로 들어오면 특정페이지로 무조건 보냄 next.config.js

async redirects() {
  return [
    {source: '/contact:path*', destination: '/form:/path*',permanent: true|
  ]
},
async reweites() {
  return [
    {source: '/api/xxxx*', destination: 'https://xxx.xxx'
  ]
},
Kyounghwan01 commented 1 year ago

Optional catch all routes


완료

Kyounghwan01 commented 1 year ago
import { useState, useEffect } from 'react';

const useMediaQuery = (query: string) => {
    const [matches, setMatches] = useState(false);

    const handleChange = (e: { matches: boolean }) => {
        setMatches(e.matches);
    };

    useEffect(() => {
        const matchQueryList = window.matchMedia(query);
        matchQueryList.addEventListener('change', handleChange);
        return () => {
            matchQueryList.removeEventListener('change', handleChange);
        };
    }, [query]);
    return matches;
};

export default useMediaQuery;
Kyounghwan01 commented 6 months ago

안드로이드 블루투스 스캔, 데이터 퍼오기, bluefi 브릿지만들기 멀티커넥션 할때 disconnect안되는거 scan에 uuid값 안넣으면 어트케 되는지

Kyounghwan01 commented 5 months ago

deeplink

      <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="telepodsee"/>
      </intent-filter>

app.tsx

import React from 'react';
import { Button, LogBox, Text, TextInput, View } from 'react-native';
import BootSplash from 'react-native-bootsplash';
import CodePush from 'react-native-code-push';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import VersionCheck from 'react-native-version-check';
import { QueryClient, QueryClientProvider } from 'react-query';

import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

import { httpClientIntegration } from '@sentry/integrations';
import * as Sentry from '@sentry/react-native';

import dayjs from 'dayjs';
import 'dayjs/locale/ko';

import { PDErrorModal, PDLoading } from '@/domains/common/components';
import GlobalDataFetcher from '@/domains/common/components/GlobalDataFetcher';
import Splash from '@/domains/common/components/Splash';
import OverlayContext from '@/domains/common/context/OverlayContext.tsx';

import { navigationRef } from '@/navigation/RootNavigation';
import RootStack from '@/navigation/RootStack.tsx';

import { sentryIgnoreError } from '@/lib/config/constants';

dayjs.locale('ko');
// OS 글자 크기 조정 제한
// @ts-ignore
if (Text.defaultProps == null) {
  // @ts-ignore
  Text.defaultProps = {};
  // @ts-ignore
  Text.defaultProps.allowFontScaling = false;
  // @ts-ignore
  TextInput.defaultProps = TextInput.defaultProps || {};
  // @ts-ignore
  TextInput.defaultProps.allowFontScaling = false;
}

if (!__DEV__) {
  CodePush.getUpdateMetadata().then(update => {
    let codepushVersion = '';
    if (update) codepushVersion = update.label;

    Sentry.init({
      dsn: 'https://6ecc487cbdfd3c5523ecfe8fd446b6f0@o4506980764483584.ingest.us.sentry.io/4506980765204480',
      tracesSampleRate: 1,
      ignoreErrors: sentryIgnoreError,
      release: `${VersionCheck.getCurrentVersion()}@${VersionCheck.getCurrentBuildNumber()}+codepush:${codepushVersion}`,
      integrations: [
        httpClientIntegration({
          failedRequestStatusCodes: [[500, 599]],
        }),
      ],
    });
  });
}

const codePushOptions = {
  checkFrequency: CodePush.CheckFrequency.MANUAL,
};

LogBox.ignoreLogs(['ViewPropTypes', 'FirmwarePrepareModule', 'code-push']);

const queryClient = new QueryClient();

const NavigationDefaultTheme = {
  ...DefaultTheme,
  colors: {
    ...DefaultTheme.colors,
    primary: 'red',
    background: '#f1f2f3',
  },
};

const NavigationDarkTheme = {
  ...DarkTheme,
  colors: {
    ...DarkTheme.colors,
    primary: 'blue',
    background: '#1C1D23',
  },
};

function App(): React.JSX.Element {
  // const isDarkMode = true; // 다크 고정
  // const { isLogin } = useUserInfoStore();
  // const { podAllList, setRecentPodVersion } = usePodStore();
  // const { setIsLoading, setLoadingMsg } = useLoadingStore();
  // const { setIsSplash, setCodePushProgress, setIsViewCodePushProgress } = useInitStore();

  // useEffect(() => {
  //   setIsLoading(false);
  //   setLoadingMsg('');
  //   setIsSplash(true);
  //   setCodePushProgress({
  //     receivedBytes: 0,
  //     totalBytes: 0,
  //   });
  //   setIsViewCodePushProgress(false);
  // }, []);

  // useEffect(() => {
  //   if (!isLogin) return;
  //   // podAllList의 length가 0이여도 기존에 실행되고있는 ble를 취소해야함으로 실행한다.
  //   handleBackgroundBleTask();
  // }, [podAllList]);

  // const handleBackgroundBleTask = async () => {
  //   try {
  //     console.log('\x1b[31m[podAllList]\x1b[0m', podAllList);
  //     const isEmulator = await DeviceInfo.isEmulator();
  //     if (isEmulator) return;

  //     const credentials = await Keychain.getGenericPassword({ service: 'access_token' });
  //     const accessToken = credentials ? credentials.password : '';

  //     accessToken && startBackground(accessToken);
  //   } catch (error) {
  //     console.log(error);
  //   }
  // };

  // const startBackground = (accessToken: string) => {
  //   Platform.OS === 'android' ? NativeModules.BleController.handleAndBleBackground(podAllList, accessToken) : NativeModules.BluetoothModule.handleBluetoothScan(podAllList, accessToken);
  // };

  // useEffect(() => {
  //   const serverHealthCheck = async () => {
  //     const { status, data } = await axiosRequest.get<{ ios: number; android: string; status: string; iosBuildNumber: number; androidBuildNumber: number; pod: string }>({ url: '/api/status' });
  //     if (status === 200) {
  //       console.log('===============[ Health Check Start ]===============');
  //       console.log('https://telepodsee.com 서버 status', status);
  //       console.log('현재버전', `${VersionCheck.getCurrentVersion()} (${VersionCheck.getCurrentBuildNumber()})`);
  //       console.log('강제업데이트 버전', data.iosBuildNumber);
  //       console.log('강제업데이트 버전', data.androidBuildNumber);
  //       if (data.pod) {
  //         setRecentPodVersion(data.pod);
  //       }

  //       // 임시 버전 체크
  //       if (Platform.OS === 'ios') {
  //         // if (VersionCheck.getCurrentBuildNumber() < data.iosBuildNumber) {
  //         //   Alert.alert('업데이트 후 테스트해주세요~!!');
  //         // }
  //       } else {
  //         if (VersionCheck.getCurrentBuildNumber() < data.androidBuildNumber) {
  //           Alert.alert('업데이트 후 테스트해주세요~!!');
  //         }
  //       }

  //       // const versionCheck = async () => {
  //       //   // 마지막 버전 체크는 마켓버전 없으면 undefined 로 나옴
  //       //   // const versionStatus = await VersionCheck.needUpdate();
  //       //   // console.log('버전 체크', versionStatus);
  //       //   // setLatest(!versionStatus?.isNeeded);
  //       //
  //       //   console.log('현재버전', VersionCheck.getCurrentVersion());
  //       //   if (VersionCheck.getCurrentBuildNumber() < 52) {
  //       //     Alert.alert('업데이트 후 테스트해주세요~!');
  //       //   }
  //       // };

  //       console.log('================[ Health Check End ]================');
  //     }
  //   };

  //   Appearance.setColorScheme('dark'); // 고정
  //   serverHealthCheck();

  //   // Push Foreground Message
  //   const unsubscribe = messaging().onMessage(async remoteMessage => {
  //     // console.log('message:', remoteMessage);
  //     await displayNotification(remoteMessage);
  //   });

  //   return unsubscribe;
  // }, []);

  // const linking = {
  //   // Prefixes accepted by the navigation container, should match the added schemes
  //   prefixes: ['telepodsee://'],
  //   // Route config to map uri paths to screens
  //   config: {
  //     // Initial route name to be added to the stack before any further navigation,
  //     // should match one of the available screens
  //     // initialRouteName: 'InitPermissionScreen' as const,
  //     // screens: {
  //     //   LoginNJoinNavigation: {
  //     //     initialRouteName: 'LoginScreen',
  //     //     screens: {
  //     //       LoginScreen: 'login',
  //     //     },
  //     //   },
  //     // },
  //   },
  // } as any;

  const linking = {
    // Prefixes accepted by the navigation container, should match the added schemes
    prefixes: ['telepodsee://'],
    // Route config to map uri paths to screens
    config: {
      // Initial route name to be added to the stack before any further navigation,
      // should match one of the available screens
      initialRouteName: 'Home' as const,
      screens: {
        // myapp://home -> HomeScreen
        Home: {
          path: 'home',
        },
        // myapp://details/1 -> DetailsScreen with param id: 1
        Profile: {
          path: 'profile/:id',
          parse: {
            id: (id: string) => `${id}`,
          },
        },
        Settings: {
          path: 'settings',
        },
      },
    },
  };

  const Stack = createNativeStackNavigator();

  return (
    <QueryClientProvider client={queryClient}>
      <SafeAreaProvider>
        <NavigationContainer
          ref={navigationRef}
          onReady={() => {
            BootSplash.hide();
          }}
          linking={linking}
        >
          <GestureHandlerRootView style={{ flex: 1 }}>
            <OverlayContext>
              <GlobalDataFetcher />
              {/* <RootStack /> */}
              <Stack.Navigator>
                <Stack.Screen name="Settings" component={DD} />
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Profile" component={DetailsScreen} />
              </Stack.Navigator>
              <PDErrorModal />
              <PDLoading />
              <Splash />
            </OverlayContext>
          </GestureHandlerRootView>
        </NavigationContainer>
      </SafeAreaProvider>
    </QueryClientProvider>
  );
}

export default __DEV__ ? App : Sentry.wrap(CodePush(codePushOptions)(App));

/**
 * https://velog.io/@minwoo129/React-Native%EC%97%90%EC%84%9C-CodePush-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
 */

function HomeScreen({ navigation }) {
  return (
    <View>
      <Text>Grocery List</Text>
      {/* FlatList to render the list of grocery items */}
      {/* <FlatList
        style={styles.list}
        data={groceryItems}
        keyExtractor={item => item.id.toString()}
        renderItem={({ item }) => (
          <TouchableOpacity style={{ padding: 10, borderBottomWidth: 1 }} onPress={() => navigation.navigate('Details', { id: item.id })}>
            <Text>{item.name}</Text>
          </TouchableOpacity>
        )}
      /> */}
    </View>
  );
}

function DD({ navigation }) {
  return (
    <View>
      <Text>DDDD List</Text>
      {/* FlatList to render the list of grocery items */}
      {/* <FlatList
        style={styles.list}
        data={groceryItems}
        keyExtractor={item => item.id.toString()}
        renderItem={({ item }) => (
          <TouchableOpacity style={{ padding: 10, borderBottomWidth: 1 }} onPress={() => navigation.navigate('Details', { id: item.id })}>
            <Text>{item.name}</Text>
          </TouchableOpacity>
        )}
      /> */}
    </View>
  );
}

function DetailsScreen({ route, navigation }) {
  return (
    <View>
      <Text>
        Item:
        {/* {groceryItems.find(item => item.id === Number(route.params.id))?.name ?? 'Not Found'} */}
      </Text>
      <Button title="Back" onPress={() => navigation.goBack()} />
    </View>
  );
}
Kyounghwan01 commented 5 months ago

npx uri-sheme add 해도 ios의 경우 백그라운드나 포그라운드에 있을때 scheme open으로 uri이 바뀌지 않는다. 그때는 https://reactnavigation.org/docs/deep-linking/#set-up-with-bare-react-native-projects 여기대로 Ios 세팅을 바꿔줘야한다.

Kyounghwan01 commented 4 months ago

code push target version이 다르면 암만 배포해도 안된다