관리 메뉴

CASSIE'S BLOG

깃허브 part 4 51강~ 본문

PROGRAMMING/React

깃허브 part 4 51강~

ITSCASSIE1107 2023. 8. 13. 12:30
import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {});

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    setEnteredEmail(event.target.value);

    setFormIsValid(
      event.target.value.includes('@') && enteredPassword.trim().length > 6
    );
  };

  const passwordChangeHandler = (event) => {
    setEnteredPassword(event.target.value);

    setFormIsValid(
      enteredEmail.includes('@') && event.target.value.trim().length > 6
    );
  };

  const validateEmailHandler = () => {
    setEmailIsValid(enteredEmail.includes('@'));
  };

  const validatePasswordHandler = () => {
    setPasswordIsValid(enteredPassword.trim().length > 6);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(enteredEmail, enteredPassword);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={enteredEmail}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={enteredPassword}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

 

pracitce/4-6

 

emailReducer에는 어떤 값이 들어온다고? state, action

 

state로 쓰던거를 다 Reducer로 대체해야한다함

 

import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const emailReducer = (state, action) => {

};

const passwordReducer = (state, action) => {
 
};

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {});
  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {});

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    setEnteredEmail(event.target.value);

    setFormIsValid(
      event.target.value.includes('@') && enteredPassword.trim().length > 6
    );
  };

  const passwordChangeHandler = (event) => {
    setEnteredPassword(event.target.value);

    setFormIsValid(
      enteredEmail.includes('@') && event.target.value.trim().length > 6
    );
  };

  const validateEmailHandler = () => {
    setEmailIsValid(enteredEmail.includes('@'));
  };

  const validatePasswordHandler = () => {
    setPasswordIsValid(enteredPassword.trim().length > 6);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(enteredEmail, enteredPassword);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={enteredEmail}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={enteredPassword}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

 

 

비밀번호도 동일하다는데 '' and null

 

import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const emailReducer = (state, action) => {

};

const passwordReducer = (state, action) => {
 
};

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: '',
    isValid: null
  });
  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
    value: '',
    isValid: null
  });

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    setEnteredEmail(event.target.value);

    setFormIsValid(
      event.target.value.includes('@') && enteredPassword.trim().length > 6
    );
  };

  const passwordChangeHandler = (event) => {
    setEnteredPassword(event.target.value);

    setFormIsValid(
      enteredEmail.includes('@') && event.target.value.trim().length > 6
    );
  };

  const validateEmailHandler = () => {
    setEmailIsValid(enteredEmail.includes('@'));
  };

  const validatePasswordHandler = () => {
    setPasswordIsValid(enteredPassword.trim().length > 6);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(enteredEmail, enteredPassword);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={enteredEmail}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={enteredPassword}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

그 다음 set으로 쓰던거를 다 주석처리해야한다.

 

email값의 초기값은 '' 빈 스트링이에요.
isValid: null
그냥 ()안에 아무것도 없으면 null이라는 뜻임

그리고 enteredEmail로 썼던 것들을 emailState의 value로 바꿀 수 있다고함.

 

주석처리 된거는 뺴고 enteredState를 emailState.value로 바꿔줘 

그리고 enteredPassword를 다 passwordState.value로 바꾸기 

 

import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const emailReducer = (state, action) => {

};

const passwordReducer = (state, action) => {
 
};

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: '',
    isValid: null
  });
  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
    value: '',
    isValid: null
  });

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    //setEnteredEmail(event.target.value);

    setFormIsValid(
      event.target.value.includes('@') && passwordState.value.trim().length > 6
    );
  };

  const passwordChangeHandler = (event) => {
   // setEnteredPassword(event.target.value);

    setFormIsValid(
      emailState.value.includes('@') && event.target.value.trim().length > 6
    );
  };

  const validateEmailHandler = () => {
    //setEmailIsValid(enteredEmail.includes('@'));
  };

  const validatePasswordHandler = () => {
    //setPasswordIsValid(enteredPassword.trim().length > 6);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(emailState.value, passwordState.value);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={emailState.value}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordIsValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={passwordState.value}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

emailIsValid도 emailState.isValid로 다 바꿔주기 passwordIsvalid도..

 

import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const emailReducer = (state, action) => {

};

const passwordReducer = (state, action) => {
 
};

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: '',
    isValid: null
  });
  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
    value: '',
    isValid: null
  });

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    //setEnteredEmail(event.target.value);

    setFormIsValid(
      event.target.value.includes('@') && passwordState.value.trim().length > 6
    );
  };

  const passwordChangeHandler = (event) => {
   // setEnteredPassword(event.target.value);

    setFormIsValid(
      emailState.value.includes('@') && event.target.value.trim().length > 6
    );
  };

  const validateEmailHandler = () => {
    //setEmailIsValid(enteredEmail.includes('@'));
  };

  const validatePasswordHandler = () => {
    //setPasswordIsValid(enteredPassword.trim().length > 6);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(emailState.value, passwordState.value);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailState.isValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={emailState.value}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordState.isValid=== false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={passwordState.value}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

하나의 reducer에서 여러가지 type의 요청이 와서 분기처리를 해줘야한다고함
이 분기처리는 action.type이라는 식으로 분기처리 할거라고함.

action.type === 'USER_INPUT' 
'USER_INPUT'은 우리가 명명하기 나름이라고함.
USER_INPUT이라는 타입이 들어왔을 때 이때 값이 바뀌는경우를 핸들링하겠다. 
어떤거를 리턴해줘야하죠? emailState
state값이 2개 잖아
값이 바껴서 이게 state에 업데이트 되야하는거니까
action에서 값을 받아와야하고
action.val 가져오고
isvalid는 @를 만족하는지
isValid: action.val.includes('@') 
만족하면 true, 만족하지 않으면 false다.

 

여기서는 다름 값은 그대로 가지고 있고 유효성 검사만 해주면 되는거임
그래서 state.value를 그냥 그대로 써주면 된다고함

return 안에 들어가있어야함

 

import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const emailReducer = (state, action) => {
  //값이 바뀌는 경우 setEnteredEmail
  if(action.type === 'USER_INPUT'){
    return {
      value: action.val,
      isValid: action.val.includes('@')
    }
  }
  //유효성 검사를 해야하는 경우 setEmailIsValid
  if(action.type === 'INPUT_BLUR'){
    return {      
      value: state.value,
      isValid: state.value.incldues("@"),}

  };


};

const passwordReducer = (state, action) => {
 
};

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: '',
    isValid: null
  });
  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
    value: '',
    isValid: null
  });

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    //setEnteredEmail(event.target.value);

    setFormIsValid(
      event.target.value.includes('@') && passwordState.value.trim().length > 6
    );
  };

  const passwordChangeHandler = (event) => {
   // setEnteredPassword(event.target.value);

    setFormIsValid(
      emailState.value.includes('@') && event.target.value.trim().length > 6
    );
  };

  const validateEmailHandler = () => {
    //setEmailIsValid(enteredEmail.includes('@'));
  };

  const validatePasswordHandler = () => {
    //setPasswordIsValid(enteredPassword.trim().length > 6);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(emailState.value, passwordState.value);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailState.isValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={emailState.value}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordState.isValid=== false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={passwordState.value}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

 

근본: dispatch안에는 action을 넣어줘야한다. 그리고 type넣어줘야함


const emailChangeHandler를 가서
dispatchEmail() 만들어서 안에 인자에 수정해라. 중괄호로 인자넣기

지금 reducer를 작업하는거다.

지금값을 그대로 validation하는거라는데?


    setFormIsValid(
      event.target.value.includes('@') && passwordState.value.trim().length > 6
이 코드가

emailState.isValid로 바뀜 

 

 

import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const emailReducer = (state, action) => {
  //값이 바뀌는 경우 setEnteredEmail
  if(action.type === 'USER_INPUT'){
    return {
      value: action.val,
      isValid: action.val.includes('@')
    }
  }
  //유효성 검사를 해야하는 경우 setEmailIsValid
  if(action.type === 'INPUT_BLUR'){
    return {      
      value: state.value,
      isValid: state.value.incldues("@"),}

  };


};

const passwordReducer = (state, action) => {
 
};

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: '',
    isValid: null
  });
  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
    value: '',
    isValid: null
  });

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    //setEnteredEmail(event.target.value);
    dispatchEmail({type: 'USER_INPUT', val:event.target.value }) //value는 또 바로 위에 event.target.value넣으면된다고함

    setFormIsValid(
      emailState.isValid && passwordState.isValid > 6
    );


  };

  const passwordChangeHandler = (event) => {
   // setEnteredPassword(event.target.value);
    dispatchPassword({type: 'USER_BLUR', val: event.target.value})

    setFormIsValid(
      emailState.value.includes('@') && event.target.value.trim().length > 6
    );
  };

  const validateEmailHandler = () => {
    //setEmailIsValid(enteredEmail.includes('@'));
    dispatchEmail({type: 'USER_BLUR'})
  };

  const validatePasswordHandler = () => {
    //setPasswordIsValid(enteredPassword.trim().length > 6);
    dispatchPassword({type: 'USER_BLUR'})
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(emailState.value, passwordState.value);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailState.isValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={emailState.value}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordState.isValid=== false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={passwordState.value}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

 

 

state는 value고 action은 val

 

위에 코드 다 엉터리 

 

다 뜨어고친 거

 

import React, { useState, useEffect, useReducer } from 'react';

import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';

const emailReducer = (state, action) => {
  //값이 바뀌는 경우 setEnteredEmail
  if(action.type === 'USER_INPUT'){
    return {
      value: action.val,
      isValid: action.val.includes('@')
    }
  }
  //유효성 검사를 해야하는 경우 setEmailIsValid
  if(action.type === 'INPUT_BLUR'){
    return {      
      value: state.value, //값은 그대로 갖고 있고 유효성검사만
      isValid: state.value.includes("@")}

  };
  return {
    value: '',
    isValid: null,
  }

};

const passwordReducer = (state, action) => {
  //값이 바뀌는 경우 setEnteredPassword
  if(action.type === "USER_INPUT"){
    return {
      value: action.val,
      isValid: action.val.trim().length > 6,
    };
  }

  //  유효성검사를 해야하는 경우, setPasswordIsValid
  if(action.type === "INPUT_BLUR"){
    return {
      value: state.value,
      isValid: state.value.trim().length > 6
    };
 
};
  return {
    value: '',
    isValid: null,
  }
}

const Login = (props) => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState('');
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);
  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: "",
    isValid: null,
  });
  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
    value: "",
    isValid: null,
  });

  useEffect(() => {
    console.log('EFFECT RUNNING');

    return () => {
      console.log('EFFECT CLEANUP');
    };
  }, []);

  // useEffect(() => {
  //   const identifier = setTimeout(() => {
  //     console.log('Checking form validity!');
  //     setFormIsValid(
  //       enteredEmail.includes('@') && enteredPassword.trim().length > 6
  //     );
  //   }, 500);

  //   return () => {
  //     console.log('CLEANUP');
  //     clearTimeout(identifier);
  //   };
  // }, [enteredEmail, enteredPassword]);

  const emailChangeHandler = (event) => {
    //setEnteredEmail(event.target.value);
    dispatchEmail({type: 'USER_INPUT', val:event.target.value }) //value는 또 바로 위에 event.target.value넣으면된다고함

    setFormIsValid(
      emailState.isValid && passwordState.isValid
    );

  };

  const passwordChangeHandler = (event) => {
   // setEnteredPassword(event.target.value);
    dispatchPassword({type: 'USER_INPUT', val: event.target.value})

    setFormIsValid(
      emailState.isValid && passwordState.isValid
    );
  };

  const validateEmailHandler = () => {
    //setEmailIsValid(enteredEmail.includes('@'));
    dispatchEmail({type: 'INPUT_BLUR'});
  };

  const validatePasswordHandler = () => {
    //setPasswordIsValid(enteredPassword.trim().length > 6);
    dispatchPassword({type: 'INPUT_BLUR'});
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(emailState.value, passwordState.value);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailState.isValid === false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="email">이메일</label>
          <input
            type="email"
            id="email"
            value={emailState.value}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordState.isValid=== false ? classes.invalid : ''
          }`}
        >
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={passwordState.value}
            onChange={passwordChangeHandler}
            onBlur={validatePasswordHandler}
          />
        </div>
        <div className={classes.actions}>
          <Button type="submit" className={classes.btn} disabled={!formIsValid}>
            로그인
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default Login;

input_blur가 지금값을 그대로 validation해주는거라는데?

 


52강

 

 

 

 

만약에 ComponentC에서 isLogin이 됐는지 안됐는지 알아야되는 상황이 온다면? 

 

 

어떻게 하면 ComponentA child에서 ComponentC로 넘어갈 수 있을까요?

 

 

부모에서 자식으로 가는거는 props로 가서 간단한데
자식에서 부모로 보내는 건 조금더 복잡하다.

 

 

그래서 componentA에도 그에 관련된 함수를 만들어야하고
그 함수를 Achild에서 받아서 함수를 실행을 시켜서
ComponentA에서 그 다음 App.js에 보내고 그 다음 props 방법을 통해서 ComponentC로 보내기 

 

전역컨텍스트 하는 개념을 쓴다고함.

 

소스코드 그대로 쓴다함. 

 

Navigation 이랑도 함

 

import React from 'react';

const AuthContext = React.createContext({
   
})

이 createContext에 들어가는 값은 초기값이다. 
이 context의 초기값.

 

 

import React from 'react';

const AuthContext = React.createContext({
    isLoggedIn: false
});

export default AuthContext;

처음에는 초기값을 false로 두는게 맞지
그러면 이게 아주 기본적은 context를 만든거란다.

 

 

isLoggedIn을 state값으로 관리하고 있는데
이거를 전역으로 관리할 수 있게
context로 넣어줘야한단다. 

 

 

 

context로 전체 component로 전체를 감싸줘야한다.

그래서 AutoContext에서 최상단 root component인 app.js  에서 한번 넣어보면 된다.

React.Fragment 배웠죠?
React return js할 때 여러개의 element를 해줄 수 없기 때문에
하나로 묶어주기 위한 도구라고 한다. 

 

 

AutoContext로 묶으면 굳이 fragment로 묶을 필요가 없다함.

 

 

 

 

provider: 제공하다. 
아까 어떤 값을 context에서 관리하기로 헀엇죠?
isLoggedIn: 
여기서 오른쪽이 
app.js에서 관리하는 이 state로 이루어진 isloggedIn을 오른쪽에 또 넣어준다.

isLoggedIn: isLoggedIn

 

 

결과: App.js에서 로그인이 되었는지 안되었는지 이런것들을 true, false 하고 있는데
값이 바뀔 때 context에서도 isLoggedIn을 받아서 가지게 됩니다. 
여러분이 로그인을 했으면 state가 isLoggedIn이 true로 바뀔 건데
그러면 context의 isloggedIn도 true로 바껴서 
그 context를 다른 component에서 쓰려고 할 때 쓸 수가 있게 됩니다.

 

 

 

context관리하는 게 useContext만 있는거는 아니란다.

 

props를 context로 한번 바꿔본다한다는데?

그러면 context는 (=useContext(AutoContext);) 이렇게
context가 isLoggedIn을 가지고 있으니까

props - > context로 바꾸기 지금 Navigation.js에서 수정하면 된다. 

 

 

import React from 'react';

import classes from './Navigation.module.css';

const Navigation = (props) => {
  const context = useContext(AutoContext);
  return (
    <nav className={classes.nav}>
      <ul>
        {context.isLoggedIn && (
          <li>
            <a href="/">사용자</a>
          </li>
        )}
        {context.isLoggedIn && (
          <li>
            <a href="/">어드민</a>
          </li>
        )}
        {context.isLoggedIn && (
          <li>
            <button onClick={props.onLogout}>로그아웃</button>
          </li>
        )}
      </ul>
    </nav>
  );
};

export default Navigation;

다 엉터리로 되서 import 제대로 해주고..AutoContext

AutoContext.Provider로 값 감싸고? 

import React, { useState } from 'react';

import Login from './components/Login/Login';
import Home from './components/Home/Home';
import MainHeader from './components/MainHeader/MainHeader';
import AuthContext from './context/AuthContext';

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const loginHandler = (email, password) => {
    // We should of course check email and password
    // But it's just a dummy/ demo anyways
    setIsLoggedIn(true);
  };

  const logoutHandler = () => {
    setIsLoggedIn(false);
  };

  return (
    <AuthContext.Provider
      value={{
        isLoggedIn:isLoggedIn
      }}>
      <MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
      <main>
        {!isLoggedIn && <Login onLogin={loginHandler} />}
        {isLoggedIn && <Home onLogout={logoutHandler} />}
      </main>
    </AuthContext.Provider>
  );
}

export default App;

 


53강

 

onLogin같은 경우에는 email, password를 받아야겠죠. 이러네
onLogin: (email, password)

처음에는 빈값들로 초기화 하기 

 

import React from 'react';

const AuthContext = React.createContext({
    isLoggedIn: false,
    onLogin: (email, password) => {},
    onLogout: () => {
       
    }
});

export default AuthContext;

 

 

 

 

 

 

 

 

직접 context에서 provider역할까지 해보는거라고 하심 

 

import React from 'react';

const AuthContext = React.createContext({
    isLoggedIn: false,
    onLogin: (email, password) => {},
    onLogout: () => {

    }
});

export const AuthContextProvider = (props) => {
    return(
        <AuthContext.Provider>
        {props.children}
       
      </AuthContext.Provider>
    )
};

props.children을 안에 넣으면

 

AuthContextProvider로 감싸면
이 안에 jsx 문법컴포넌트들이 그대로 안에 내려올 수 있게 한다. 

 

 

import React from 'react';

const AuthContext = React.createContext({
    isLoggedIn: false,
    onLogin: (email, password) => {},
    onLogout: () => {

    }
});

export const AuthContextProvider = (props) => {
    return(
        <AuthContext.Provider value={{
            isLoggedIn: isLoggedIn,
            onLogout: logoutHandler,
            onLogin: LoginHandler
        }}>
        {props.children}
       
      </AuthContext.Provider>
    )
};

export default AuthContext

App.js에 logIn, logout, loginhandler, logouthandler 가 있어도 정상적으로 들어감
하지만 app파일은 root에서 모든것들을 총 관리하는 컴포넌트이기 때문에
여기에 많은 로직이 들어가게되있으면 굉장히 부담스럽단다.

 

 

 

 

ref를 내려준다고 쓸 수 있는게 아니고
한단계 더 작업을 해야 쓸 수 있단다.

 

 

 

 

 

부모컴포넌트에서 자식컴포넌트로 props로 내려줄 때
props가 바뀔 때마다 자식컴포넌트가 rerendering이 일어나는거는 
부모component에서 컨트롤을 할 수 있단다.

 

 

memo로 컴포넌트를 감싼다.
memo(SomeComponent, arePropsEqual?)

 

 

 

 

알고리즘 공부를했다면 들어봤을 거란다.
memoization은 말 그대로 기억하는거란다. 

 

memoization: 이전에 연산해논거를 기억하는거다. 

 

 

props가 바뀌면 리렌더링이 당연히 일어나는데
우리가 부모컴포넌트를 만들고 자식컴포넌트를 만들 때
굳이 리렌더링을 안시켜줘도되는데 리렌더링이 될 때가 있어요.
"불필요하게" 
이 말이 굉장히 중요한데

불필요하게 리렌더링되는 경우에 그런 것들이 안 일어나게 memoization해주겠다라는 뜻이란다.

-props가 바뀌지않을 때 렌더링을 생략한다. 
-state를 사용할 때 memoized된 component를 업데이트한다. 

 

memo는 컴포넌트가 실행이 될 때 컴포넌트를 기억하고있어 어디에 저장한다.
그 컴포넌트가 다시 실행이 되야할 때 둘의 값 비교해서 둘의 값 똑같으면 실행을 안 시켜주고
차이가 있으면 실행을 하는거다. 저장할 떄 메모리 비용이 든다. 그리고 두개의 값을 비교할 때도 연산의 비용이 든다.

 


57강~

 

2강에서 배웠다는데 자바스크립트에서는 원시타입과 객체타입이 있단다.

 

필요한 컴포넌트만 렌더링은 같은 말로 추상화수준이 높다라는 말이랑 똑같다고함.

 

컴포넌트 기반 = 레고랑 비슷 

 

리플로우와/리페인딩은 브라우저 렌더링의 한 단계란다. 여기까지 

 

object같은 경우는 힙에 그 값들이 저장이 되고 그 힙에 메모리 주소를 콜스택에서 저장을 해서
object변수가 그 메모리 주소를 값으로 가지고 판단을 한다. 

 

 

캐쉬: 쉽게 얘기하면 가까이에 있는 메모리 공간이라고함. 

 

가까이에 있기때문에 빨리 가져올 수 있다고함. 

 

 

함수가 useCallback의 첫번째 인자로 들어가져있는 걸 볼 수 있다.

 

두 번째 인자는 의존성
useEffect할때도 의존성 배웠데
이 값들이 이 hook들이 이 값에 대해서 의존을 하고 있어서
이 값들이 바뀔 때마다 이 함수가 매번 실행이 된다. 
여기서는 productId가 바뀔 때 마다 앞에 저 함수가 매번 실행이 된다. 

 

그리고 referrer 가 바뀔 때 마다 함수가 실행되는거임.

 

클로저에 대한 개념을 정말 여러가지로 정의하고 있지만 가장 쉽고 명확하게 정리한 개념이 있다.

en: closure makes it possible for a function to have "private" variables.
ko: 클로저는 함수에 "사적인" 변수를 가질 수 있도록 해준다.

우리가 평소에 들었던 캡슐화 라는 개념이 바로 여기서 등장한다. 어떻게 보면 상태를 가지고 있다고도 말할 수 있다.

 

 

 

 

자식컴포넌트가 리렌더링이 되지않게 하려면
이렇게 useCallback를 쓰면 된다는데 이게 after.

 

12강에서 나온 거

 

14강에서 나옴

 

 

그러면 스프레드 연산자 쓰면 1,2,3이 붙어서 5,6,1,2,3이 되는거지. 

 

 

문자열을 스프레드 연산자에 넣고 배열로 감싸면 어떤 결과가 나는지?

문자의 열이라서 문자 하나하나하를 배열로 쪼개져서 만든단다. 

 

a=1, b=2일 때 그냥 others 뭉뚱그려서 할 수도 있다고함.

 

클로저 16강에서 설명해줌

 

 

 

 

객체안에 메소드 있는거는 계속 다뤘던 거라고함.
this키워드 할 떄 특히 많이 다뤘다고함.

 

object의 메소드에도 함수표현식을 넣을 수 있는데 이 역시도 object도 key, value형태 value부분에 즉 값 부분에 함수를 넣었다.
이 역시에  자바스크립트가 함수를 일급시민으로 인식하고 있기 때문에 가능한 문법이라고 함.

 

addEventListner가 고차함수에 속한다 왜냐하면 두 번째 인자로 함수를 받기 때문이다.

 

count함수같은 경우에도 function을 return하는 것을 볼 수 있다.
자바스크립트에서는 함수를 리턴하는 것도 가능하다 이렇게 알면 된데. 

 

처음에 자바스크립트 공부할 때 쌤도 this, 클로저가 고비였다함.

 

node app.js가 파일을 실행시키는거다,

 

지금 아무것도 실행이 되지않죠. 왜냐면
booker가 호출을 해주지않았기 때문에 라는데?
이해안됨....

 

자바스크립트 엔진에 call stack이 있었고 
call stack에는 전역 또는 함수마다 실행컨텍스트가 쌓인다.

 

scope는 실행컨텍스트가 만들어질 떄마다 말그대로 범위를 가진다. 
그 변수가 영향을 줄 수 있는 범위를 스코프라고한다. 진짜 중요한 개념이다.

 

passengerBooking()이 실행이 되서 booker에 할당이 된거란다. 

 

그리고 이 함수가 실행된 시점에서
passengerBooking 실행컨텍스트가 스택에 쌓인단다.
그리고 그 실행컨텍스트가 쌓이기 때문에
거기에 맞춰서 passengerBooking이라는 스코프가 또 하나가 생기게 된 것 

 

passengerBooking에 변수가 몇개가 있죠? 

1개있는거 아님? passengerCount?

응 맞아 1개가 있고 passengerCount

 

 

밑에 passengerBooking(); 되면 리턴까지 실행이 되는데 함수가 리턴이 되면 그 실행 컨텍스트는 콜스택에서 빠진다 즉 사라진다. 그래서 그 스코프도 사라진단다.&nbsp;빠지니까 이제 접근할 수가 없단다.

 

let으로 passengerCount라는 변수를 선언을 해줬고
이 변수는 passengerBooking이라는 스코프에 들어가있었는데
passengerBooking은 아래 
const booker = passengerBooking()에서 이미 실행이 끝나서 
이미 빠져나왔고 이제는 passengerCount라는 변수에 접근할 수가 없다. 

 

 

 

즉 passengerBooking이 만들어지는 시점
그리고 booker가 만들어지는 시점
을 함수는 기억한다는 뜻이다.

booker는 만들어지는 시점에서는 passengerBooking이라는 실행컨텍스트가있었죠

즉 booker라는 함수가 만들어지는 시점에서 passengerBooking실행컨텍스트를 기억하기 때문에
 passengerBooking실행컨텍스트가 빠져도 passengerCount 변수환경에 접근할 수 있다.

 passengerCount 이 변수를 booker 스코프에서 참조할 수 있단다..

아 어렵다!!!!!!
이게 가능한게 클로저 때문이다. 

 

 

58강~

 


56강~ 

 

 

foo는  = () => {}; 이 함수라서 값이 바뀌지가 않는다고함.

 

branch 4-6은 53강이랑 이어진다. 

 

53강 다시 돌아가.

 

 

 

 

Context 오브젝트에 포함된 React 컴포넌트인 Provider는 context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 합니다. Provider 컴포넌트는 value prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달합니다.

 

 

context의 많은 내용 넣어서 - > app.js나 다른 파일에서 조금 더 context를 신경 "덜" less 하게
필요한 것만 딱딱 받고 보내줄 수 있게 깔끔하게 바꿔본다고함.

이 코드 오류나긴 하는데 로그인을 하는 함수 로그아웃을 하는 함수 넣어준거다. 

 

import React from 'react';

const AuthContext = React.createContext({
    isLoggedIn: false,
    onLogin: (email, password) => {},
    onLogout: () => {

    }
});

export const AuthContextProvider = (props) => {
    return(
        <AuthContext.Provider value={{
            isLoggedIn: isLoggedIn,
            onLogout: logoutHandler,
            onLogin: LoginHandler
        }}>
        {props.children}
       
      </AuthContext.Provider>
    )
};

export default AuthContext;

 

보니까 

로그인같은 경우에는 이메일이랑 패스워드만들어야해서 인자를 넣었는데 나는 안 넣었었네? 
밑에 코드가 맞다.

로그아웃은 처음에는 빈값들로 초기값을 작성해주도록 하겠습니다.

 

 

 

Provider를 app.js에서 하고 있었는데 이제 AutoContext.js에서 한다고함. 
그리고 로그인이 됐을 때 로그인이 되어있는지 안되어있는지 
로그인을 불필요하게 여러번 할 필요가 없다고
useEffect 로 첫 로그인시에 로그인을 판단해주는 로직을 추가해본다고함 

 

 

export const AuthContextProvider = (props) => {
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    //로그인 상태 확인해주는 로직
    useEffect(() => {
        const storedUserLoggedInInfo = localStorage.getItem('isLoggedIn');
    }, []);

    const loginHandler = (email, password) => {

      setIsLoggedIn(true);
    };
 
    const logoutHandler = () => {
      setIsLoggedIn(false);
    };


    return(
        <AuthContext.Provider value={{
            isLoggedIn: isLoggedIn,
            onLogout: logoutHandler,
            onLogin: LoginHandler
        }}>
        {props.children}
       
      </AuthContext.Provider>
    )
};

 

 

 

 

import React from 'react';

const AuthContext = React.createContext({
    isLoggedIn: false,
    onLogin: (email, password) => {},
    onLogout: () => {

    }
});

export const AuthContextProvider = (props) => {
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    //로그인 상태 확인해주는 로직
    useEffect(() => {
        const storedUserLoggedInInfo = localStorage.getItem('isLoggedIn');
        if(storedUserLoggedInInfo === '1') setIsLoggedIn(true)
        //false는 초기값이라 굳이 쓸 필요 x
    }, []);

    const loginHandler = (email, password) => {

      setIsLoggedIn(true);
    };
 
    const logoutHandler = () => {
      setIsLoggedIn(false);
    };


    return(
        <AuthContext.Provider value={{
            isLoggedIn: isLoggedIn,
            onLogout: logoutHandler,
            onLogin: LoginHandler
        }}>
        {props.children}
       
      </AuthContext.Provider>
    )
};

export default AuthContext;

 

 

 

 

 

 

 

 

그니까  계속 오류가 뜨지.

 

 

 

 

 

 

 

 

 

와 import 안 되어있으면 다시 태그 지워서 자동완성으로 딱 하면 자동으로 import 된다!!! 와!!! 해결!!

 

 

기존에는 AuthContext를 export를 해서 app.js에서 authContext.Provider로 사용하면서
value값들을 직접 넣어주면서 했는데 이제는 AuthContext 를 크게 만들어줬어요. 
이 안에 함수, 값들, 전역으로 관리해야하는 것들, 로직도 다 넣고 
필요한 곳에 가져가서 import해서 쓰기만 하면 된다.
감싸주는 것들은 가장 root directory에서 감싸주면 되고
사용하는 것들은 여러분들이 쓰는 컴포넌트안에서 useContext로 쓰면 된다. 

 

 

 

useReducer – React

 

useReducer – React

The library for web and native user interfaces

react.dev

 

 

쌤: 지난 강의에서 배웠죠. Reducer를 통해서 어떻게 복잡한 state를 
여러가지 type으로 액션과 타입으로 처리할 수 있는지 배웠다고함.
deep down 깊게 아래로 돌아갈 수 있게해준다고 Context가

context랑 reducer랑 같이 써본다고함.

 

54강 forwardRefs

 

 

54강

 

 

ref는 current 속성을 가지는 객체다. 직접 만들어도 되지만 ({current: null}로 생성해도 되는 것 같다) 리액트 API를 사용하지 않을 이유가 없다.

 

 

60강

 

 

 

 

 

props가 바뀌지않았을 때 skip하게 해준다는데? 
컴포넌트가 리렌더링이 되는 여러가지 조건이 있었다.
컴포넌트 안에서 state가 바뀔 때 리렌더링이 일어나고
부모컴포넌트로부터 자식컴포넌트로 전달해주는 props가 "바뀔 때에도" 일어나고
context가 바뀔 때도 일어나고
리렌더링이 일어나는 것들은 컴포넌트 바깥에서는 어쩔 수 없다는데?
컴포넌트 안에서 state를 쓰든 context를 쓰든 여튼 컴포넌트안에서 재실행이 되는 것들은 
컴포넌트 외부에서는 어쩔 수 없단다. 

 

 

 

그런데!! 
컴포넌트 외부에서 부모컴포넌트에서 자식컴포넌트로
props를 내려주는데 그 props가 바뀔 떄마다
자식컴포넌트가 리렌더링이 일어나는거는
부모컴포넌트에서 컨트롤 할 수 있다하는데?
그방법으로 사용하는 것이 리액트의 메모라고함.

 

알렉스란 이름으로 props를 내려줬는데
props가 brian으로 바꼈다. 
그러면 당연히 리렌더링이 일어난다. props가 바꼈으니까 
근데 부모컴포넌트를 만들고 자식 컴포넌트를 만들 때
굳이 리렌더링을 안 시켜줘도 되는데
불필요하게 리렌더링이 일어날 때가 있다. 
그때 그거를 불필요하게 안 일어나게 하겠다. 그게 memoization이다.

 

56강은 따로 브랜치 ㄴㄴ 이어서 그냥 옛날 코드에 

반응형