인프런 커뮤니티 질문&답변
게시글 작성 오류입니다!
해결된 질문
작성
·
620
답변 14
1
0
0
lcm2822
질문자
제로초님
import부분에서
const [text, setText, onChangeText] = useInput('');
const [text, onChangeText, setText] = useInput('');
위 부분을 아래처럼 바꾸었더니
기존에 콘솔에서 아무것도 찍히지 않았던 콘솔이 아래의 그림처럼 나오고 이제 정상적으로 타이핑이 가능합니다!
혹시 이유를 알 수 있을까요?

0
0
lcm2822
질문자
useInput.js
import { useState, useCallback } from 'react';
export default (initValue = null) => {
const [value, setter] = useState(initValue);
const handler = useCallback((e) => {
console.log('문제점 시작');
console.log(e.target.value);
console.log('문제점 끝');
setter(e.target.value);
}, []);
return [value, handler, setter];
};
value={text}를 넣었을 때 콘솔입니다.
value={text}를 주석처리했을 때 콘솔입니다.

아이디와 비밀번호 둘 다 정상적으로 나왔습니다.
0
제로초(조현영)
지식공유자
네 value 넣는게 문제라는 것을 알고 있고요. 그러니까 e.target.value를 console.log 하는게 매우 중요합니다. 그걸 보여주셔야 해요.
0
lcm2822
질문자
input.TextArea 부분의 문제로 판단됩니다.
<Input.TextArea
value={text}
onChange={onChangeText}
maxLength={140}
placeholder="어떤 신기한 일이 있었나요?"
/>
이 코드에서
1.maxLength 부분을 제거하면 처음 입력 했을 때 아래의 그림처럼 나오고 다른 입력이 불가능합니다.

2. value={text}부분을 제거하면 정상적으로 아래의 그림처럼 나타납니다.

value={text} 부분이 문제가 있다고 판단했습니다. 혹시 value값을 대체허거나 제거할 수 있을까요?
0
제로초(조현영)
지식공유자
setter(e.target.value) 위에 console.log(e.target.value) 넣으셔서 onChange시 값이 들어오는지 확인해보세요.
다시 태그들을 하나씩 넣어서 어떤 태그나 props를 추가할 때 문제가 되는지 확인해보세요.
0
lcm2822
질문자
제가 이해하는 것에 문제가 있어 return 부분에서 useInput부분수정을 못 하였으나 Input.Textarea시도하는 것은 해보았는데
글씨 작성이 되었습니다!
혹시 몰라서 코드 올려봅니다.
useInput.js
import { useState, useCallback } from 'react';
export default (initValue = null) => {
const [value, setter] = useState(initValue);
const handler = useCallback((e) => {
setter(e.target.value);
}, []);
return [value, handler, setter];
};
PorstForm.js return부분return (
<Input.TextArea />
);
타자가 쳐지는 그림

0
제로초(조현영)
지식공유자
코드 상으로는 문제가 잘 안 보이는데요. useInput.js 가셔서 onChange에서 setValue 위에 console.log(e.target.value) 찍어보세요. 그리고 return 부분에 Form 같은 다른 거 다 빼고 Input.Textarea만 남기고 시도해보세요.
0
lcm2822
질문자
post관련 front 코드입니다.
PostForm.js
import React, { useCallback, useEffect, useRef } from 'react';
import { Form, Input, Button } from 'antd';
import { useSelector, useDispatch } from 'react-redux';
import { addPost } from '../reducers/post';
import useInput from '../hooks/useInput';
const PostForm = () => {
const dispatch = useDispatch();
const [text, setText, onChangeText] = useInput('');
const { imagePaths, addPostLoading, addPostDone } = useSelector((state) => state.post);
const imageInput = useRef();
const onClickImageUpload = useCallback(() => {
imageInput.current.click();
}, [imageInput.current]);
useEffect(() => {
if (addPostDone) {
setText('');
}
}, [addPostDone]);
const onSubmit = useCallback(() => {
dispatch(addPost(text));
}, [text]);
return (
<Form style={{ margin: '10px 0 20px' }} encType="multipart/form-data" onFinish={onSubmit}>
<Input.TextArea
value={text}
onChange={onChangeText}
maxLength={140}
placeholder="어떤 신기한 일이 있었나요?"
/>
<div>
<input type="file" multiple hidden ref={imageInput} />
<Button onClick={onClickImageUpload}>이미지 업로드</Button>
<Button type="primary" style={{ float: 'right' }} htmlType="submit" loading={addPostLoading}>짹짹</Button>
</div>
<div>
{imagePaths.map((v) => (
<div key={v} style={{ display: 'inline-block' }}>
<img src={`http://localhost:3065/${v}`} style={{ width: '200px' }} alt={v} />
<div>
<Button>제거</Button>
</div>
</div>
))}
</div>
</Form>
);
};
export default PostForm;
sagas/post.js
import axios from 'axios';
import { call,all, delay, fork, put, takeLatest, throttle } from 'redux-saga/effects';
import {
ADD_COMMENT_FAILURE,
ADD_COMMENT_REQUEST,
ADD_COMMENT_SUCCESS,
ADD_POST_FAILURE,
ADD_POST_REQUEST,
ADD_POST_SUCCESS,
generateDummyPost,
LOAD_POSTS_FAILURE,
LOAD_POSTS_REQUEST,
LOAD_POSTS_SUCCESS,
REMOVE_POST_FAILURE,
REMOVE_POST_REQUEST,
REMOVE_POST_SUCCESS,
} from '../reducers/post';
import { ADD_POST_TO_ME, REMOVE_POST_OF_ME } from '../reducers/user';
function loadPostsAPI(data) {
return axios.get('/api/posts', data);
}
function* loadPosts(action) {
try {
// const result = yield call(loadPostsAPI, action.data);
yield delay(1000);
yield put({
type: LOAD_POSTS_SUCCESS,
data: generateDummyPost(10),
});
} catch (err) {
console.error(err);
yield put({
type: LOAD_POSTS_FAILURE,
data: err.response.data,
});
}
}
function addPostAPI(data) {
return axios.post('/post', data);
}
function* addPost(action) {
try {
const result = yield call(addPostAPI, action.data);
yield put({
type: ADD_POST_SUCCESS,
data: result.data,
});
yield put({
type: ADD_POST_TO_ME,
data: result.data.id,
});
} catch (err) {
console.error(err);
yield put({
type: ADD_POST_FAILURE,
data: err.response.data,
});
}
}
function removePostAPI(data) {
return axios.delete('/api/post', data);
}
function* removePost(action) {
try {
// const result = yield call(removePostAPI, action.data);
yield delay(1000);
yield put({
type: REMOVE_POST_SUCCESS,
data: action.data,
});
yield put({
type: REMOVE_POST_OF_ME,
data: action.data,
});
} catch (err) {
console.error(err);
yield put({
type: REMOVE_POST_FAILURE,
data: err.response.data,
});
}
}
function addCommentAPI(data) {
return axios.post(`/post/${data.postId}/comment`, data);
}
function* addComment(action) {
try {
const result = yield call(addCommentAPI, action.data);
yield put({
type: ADD_COMMENT_SUCCESS,
data: result.data,
});
} catch (err) {
yield put({
type: ADD_COMMENT_FAILURE,
data: err.response.data,
});
}
}
function* watchLoadPosts() {
yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts);
}
function* watchAddPost() {
yield takeLatest(ADD_POST_REQUEST, addPost);
}
function* watchRemovePost() {
yield takeLatest(REMOVE_POST_REQUEST, removePost);
}
function* watchAddComment() {
yield takeLatest(ADD_COMMENT_REQUEST, addComment);
}
export default function* postSaga() {
yield all([
fork(watchAddPost),
fork(watchLoadPosts),
fork(watchRemovePost),
fork(watchAddComment),
]);
}
reducers/post.js
import shortId from 'shortid';
import faker from 'faker';
import produce from '../util/produce';
export const initialState = {
mainPosts: [],
imagePaths: [],
hasMorePosts: true,
loadPostsLoading: false,
loadPostsDone: false,
loadPostsError: null,
addPostLoading: false,
addPostDone: false,
addPostError: null,
removePostLoading: false,
removePostDone: false,
removePostError: null,
addCommentLoading: false,
addCommentDone: false,
addCommentError: null,
};
export const generateDummyPost = (number) => Array(number).fill().map(() => ({
id: shortId.generate(),
User: {
id: shortId.generate(),
nickname: faker.name.findName(),
},
content: faker.lorem.paragraph(),
Images: [{
src: faker.image.image(),
}],
Comments: [{
User: {
id: shortId.generate(),
nickname: faker.name.findName(),
},
content: faker.lorem.sentence(),
}],
}));
export const LOAD_POSTS_REQUEST = 'LOAD_POSTS_REQUEST';
export const LOAD_POSTS_SUCCESS = 'LOAD_POSTS_SUCCESS';
export const LOAD_POSTS_FAILURE = 'LOAD_POSTS_FAILURE';
export const ADD_POST_REQUEST = 'ADD_POST_REQUEST';
export const ADD_POST_SUCCESS = 'ADD_POST_SUCCESS';
export const ADD_POST_FAILURE = 'ADD_POST_FAILURE';
export const REMOVE_POST_REQUEST = 'REMOVE_POST_REQUEST';
export const REMOVE_POST_SUCCESS = 'REMOVE_POST_SUCCESS';
export const REMOVE_POST_FAILURE = 'REMOVE_POST_FAILURE';
export const ADD_COMMENT_REQUEST = 'ADD_COMMENT_REQUEST';
export const ADD_COMMENT_SUCCESS = 'ADD_COMMENT_SUCCESS';
export const ADD_COMMENT_FAILURE = 'ADD_COMMENT_FAILURE';
export const addPost = (data) => ({
type: ADD_POST_REQUEST,
data,
});
export const addComment = (data) => ({
type: ADD_COMMENT_REQUEST,
data,
});
// 이전 상태를 액션을 통해 다음 상태로 만들어내는 함수(불변성은 지키면서)
const reducer = (state = initialState, action) => produce(state, (draft) => {
switch (action.type) {
case LOAD_POSTS_REQUEST:
draft.loadPostsLoading = true;
draft.loadPostsDone = false;
draft.loadPostsError = null;
break;
case LOAD_POSTS_SUCCESS:
draft.loadPostsLoading = false;
draft.loadPostsDone = true;
draft.mainPosts = draft.mainPosts.concat(action.data);
draft.hasMorePosts = action.data.length === 10;
//draft.mainPosts = action.data.concat(draft.mainPosts);
//draft.hasMorePosts = draft.mainPosts.length < 50;
break;
case LOAD_POSTS_FAILURE:
draft.loadPostsLoading = false;
draft.loadPostsError = action.error;
break;
case ADD_POST_REQUEST:
draft.addPostLoading = true;
draft.addPostDone = false;
draft.addPostError = null;
break;
case ADD_POST_SUCCESS:
draft.addPostLoading = false;
draft.addPostDone = true;
draft.mainPosts.unshift(action.data);
break;
case ADD_POST_FAILURE:
draft.addPostLoading = false;
draft.addPostError = action.error;
break;
case REMOVE_POST_REQUEST:
draft.removePostLoading = true;
draft.removePostDone = false;
draft.removePostError = null;
break;
case REMOVE_POST_SUCCESS:
draft.removePostLoading = false;
draft.removePostDone = true;
draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data);
break;
case REMOVE_POST_FAILURE:
draft.removePostLoading = false;
draft.removePostError = action.error;
break;
case ADD_COMMENT_REQUEST:
draft.addCommentLoading = true;
draft.addCommentDone = false;
draft.addCommentError = null;
break;
case ADD_COMMENT_SUCCESS: {
const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
post.Comments.unshift(action.data);
draft.addCommentLoading = false;
draft.addCommentDone = true;
break;
// const postIndex = state.mainPosts.findIndex((v) => v.id === action.data.postId);
// const post = { ...state.mainPosts[postIndex] };
// post.Comments = [dummyComment(action.data.content), ...post.Comments];
// const mainPosts = [...state.mainPosts];
// mainPosts[postIndex] = post;
// return {
// ...state,
// mainPosts,
// addCommentLoading: false,
// addCommentDone: true,
// };
}
case ADD_COMMENT_FAILURE:
draft.addCommentLoading = false;
draft.addCommentError = action.error;
break;
default:
break;
}
});
export default reducer;
0
0
0








