간단한 반응이 상하문에서 통제력을 잃을 때
40603 단어 reactreduxjavascript
TL;박사:
useEffect
심사숙고하세요.useEffects
를 조심해야 한다는 것이다.useReducer
으로 하겠습니다.간단하게 시작합시다.
우리 팀은 새로운 React 프로그램을 시작했습니다. React 상하문 API 단순 useState
을 사용하면 어떻게 될지 보고 싶습니다.우리는 또한 모든 상하문을 유사한 데이터의 '상자' 로 간주하기를 희망한다.
응용 프로그램이 두 개의 컨텍스트를 필요로 할 정도로 발전했다고 가정해 봅시다.
const AuthContext = React.createContext();
const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState();
const [isLoggedIn, setIsLoggedIn] = useState();
const state = { user, isLoggedIn };
return (
<AuthContext.Provider value={{ state, setUser, setIsLoggedIn }}>
{children}
</AuthContext.Provider>
);
};
AuthContext
는 신분 검증과 관련된 상태를 포함한다.사용자가 로그인할 때 setIs LoggedIn (true) 과 setUser ({email,username}) 함수를 호출합니다.이것은 AuthContext
의 상태를 바꾸고 응용 프로그램에서 천천히 전파할 수 있다.const TimelineContext = React.createContext();
const TimelineContextProvider = ({ children }) => {
const [posts, setPosts] = useState([]);
// For the purposes of this blog, selectedPost will be used to display
// the "show page"
const [selectedPost, setSelectedPost] = useState(null);
// And let's imagine we want to do the same thing for a comment.
const [selectedComment, setSelectedComment] = useState(null);
const state = { posts, selectedPost, selectedComment };
return (
<TimelineContext.Provider
value={{ state, setPosts, setSelectedPost, setSelectedComment }}
>
{children}
</TimelineContext.Provider>
);
};
TimelineContext
는 posts
, aselectedPost
와 aselectedComment
의 목록을 포함하여 우리의 시간선 유지 보수 상태를 제공할 것입니다.이것들은 모두 매우 간단하다, 그렇지?
그 중 하나는 모든 위아래 문장의 반환값이다.현재 우리는 우리가 새로운 상태를 추가하면서 반환치의 증가가 매우 빠르다는 것을 알 수 있다.
우리 계속
TimelineContext
에서 이 문제를 해결합시다. const TimelineContextProvider = ({ children }) => {
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(null)
const [selectedComment, setSelectedComment] = useState(null)
const state = { posts, selectedPost, selectedComment };
const actions = { setPosts, setSelectedPost, setSelectedComment }
return (
<TimelineContext.Provider value={{ state, actions}}>
{children}
</TimelineContext.Provider>
);
};
그래.이게 좀 도움이 돼요.귀환 대상은 state
과actions
로 제한됩니다.또 다른 고민거리는 이런 상황이 갈수록 심각해지면 된다는 것이다.우리가 추가한 데이터가 많을수록
useStates
관리하기가 더욱 어렵다.이것은 여러 가지 배경이 있는 생각이다.우리는 관심사를 명확하게 분리할 수 있다.새로운 요구!
현재 프로그램에서 선택한 댓글과 댓글을 설정하고 싶습니다.댓글이 댓글에 달려 있다면 새 댓글을 선택할 때 취소해야 합니다selectedComment
.
이것은 상당히 간단하다.우리는 직접 useEffect
와 큰 소리를 낼 수 있다.
const TimelineContextProvider = ({ children }) => {
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(null)
const [selectedComment, setSelectedComment] = useState(null)
const state = { posts, selectedPost, selectedComment };
const actions = { setPosts, setSelectedPost, setSelectedComment }
useEffect(() => {
setSelectedComment(null)
}, [selectedPost])
return (
<TimelineContext.Provider value={{ state, actions}}>
{children}
</TimelineContext.Provider>
);
};
더 많은 수정!!!
현재, 테스트 목적으로 초기 {SelectedPost와 SelectedComment}를 추가하려고 합니다.어리석고 간단하다.아니면 이렇게?
현재 설정에 따라useEffect
는 처음 렌더링할 때initialSelectedComment
를 null
로 설정합니다.오, 부작용 없어!!!
그래서 우리의 배경은 다음과 같다.
const TimelineContextProvider = ({
initialSelectedPost,
initialSelectedComment,
children
}) => {
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(initialSelectedPost);
const [selectedComment, setSelectedComment] = useState(
initialSelectedComment
);
const state = { posts, selectedPost, selectedComment };
const actions = { setPosts, setSelectedPost, setSelectedComment };
useEffect(() => {
if (initialSelectedPost != initialSelectedComment) {
setSelectedComment(null);
}
}, [selectedPost]);
return (
<TimelineContext.Provider value={{ state, actions }}>
{children}
</TimelineContext.Provider>
);
};
이것은 큰 문제가 아닐 수도 있지만, 그것은 우리로 하여금 단지 상태를 바꾸는 것만으로 발생할 수 있는 어떤 결과도 고려하지 않을 수 없게 할 것이다.
전 세계 진상의 단일한 출처
팀의 불평 중 하나는 "구성 요소에서 어떤 {X} 상하문을 사용해야 합니까?"이다.AuthContext
와 TimelineContext
는 모두 전역 상태의 일부이기 때문에 하나의 해결 방안은 그것들을 조합하여state 대상 내의 영역을 분리하는 것이다.우리 이 문제를 해결하는 것부터 시작합시다.
const AppContextProvider = ({
initialSelectedPost,
initialSelectedComment,
children
}) => {
const [user, setUser] = useState();
const [isLoggedIn, setIsLoggedIn] = useState();
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(initialSelectedPost);
const [selectedComment, setSelectedComment] = useState(
initialSelectedComment
);
const state = {
auth: { user, isLoggedIn },
timeline: { posts, selectedPost, selectedComment }
};
const actions = {
setUser,
setIsLoggedIn,
setPosts,
setSelectedPost,
setSelectedComment
};
useEffect(() => {
if (initialSelectedPost != initialSelectedComment) {
setSelectedComment(null);
}
}, [selectedPost]);
return (
<AppContext.Provider value={{ state, actions }}>
{children}
</AppContext.Provider>
);
};
내가 보기엔 큰 승리는 아니었지만, 지금은 팀이 더 즐거워졌다.
광희의 부작용
React hooks와 1년 동안 합작한 후에 나는 어떤 환경에서useEffect
가 나쁜 생각일 수도 있다는 결론을 얻었다.(겸사겸사 한마디 하자면, 나는 네가 이 일을 한 예를 보고 싶다.
내가 얻은 더욱 구체적인 규칙은 우리의 응용 프로그램에서 전역 상태에 의존해서는 안 된다는 것이다. useEffect
나는 이것이 날카로운 칼로 너의 눈을 쉽게 찔러 뚫을 수 있다고 생각한다.그것은 하루하루 앞장서서 일하지 않는 사람들을 위해 프로젝트에서 일하는 데 장애를 증가시켰다.코드 라이브러리에서 일하는 사람들에게도 명심해야 할 일이다."{X} 을 변경하면 이 리셋이 실행될 것입니다. 수정해야 합니까?"
나의 해결 방안은 전역 상태에서 시종일관 (보통 95%) 사용하고 useReducer
전역 상태의 일부분에 의존하지 않는 것이다.
가자!
초기 상태
우선, 우리는 응용 프로그램의 초기 상태부터 시작할 것이다.
const initialState = {
auth: { user: null, isLoggedIn: false },
timeline: { posts: [], selectedPost: null, selectedComment: null }
};
그것은 매우 쉽다!우리의 초기 상태를 정의하면 우리의 모든 전 세계 상태를 한눈에 볼 수 있다.언제든지 전체 상태에 뭔가를 추가하고 싶을 때 useEffect
대상에 합리적인 기본값을 추가할 수 있습니다.예를 들어 initialState
는 처음에는 가짜였고 isLoggedIn
는 처음에는 공수조였다.
사랑하는 화생
Reducer 모드에서 내가 가장 좋아하는 부분은 Reducer의 모든 동작을 응용 프로그램과의 단일한 상호작용으로 볼 수 있다는 것이다.이러한 상호작용은 네트워크 요청이나 사용자 이벤트일 수 있습니다.동작을 설정할 때 "{X}가 발생할 때 상태가 어떻게 변합니까?"라고 묻습니다.그리고 정확한 유효 하중과 팔동작만 사용하면 이 조작을 수행할 수 있다.완성!현재 같은 상호작용이 두 곳에서 발생한다면 다른 구성 요소를 열고 논리를 기억할 필요가 없다.너는 행동만 발표하면 된다.
상하문posts
부분에 대해 우리는 두 가지 상호작용이 있는데 그것이 바로 로그인과 로그아웃이다.
코드 좀 봅시다.
const ActionTypes = {
SET_USER: "set-user",
LOGOUT_USER: "logout-user",
}
const reducer = (state, action) => {
switch (action.type) {
case ActionTypes.SET_USER: {
return {
...state,
auth: { ...state.auth, user: action.payload, isLoggedIn: true }
};
}
case ActionTypes.LOGOUT_USER: {
return {
...state,
auth: { ...state.auth, user: null, isLoggedIn: false }
};
}
...
}
};
와, 이거 K.I.s.s.:D야.
현재 우리는 호출auth
과 setUser
를 기억할 필요가 없다. 우리는 주어진 상호작용에 상응하는 조작을 할당하기만 하면 된다.
다음은 setIsLoggedIn
상태에 대한 조작을 추가합니다.
const ActionTypes = {
...,
ADD_POSTS: "add-posts",
SELECT_POST: "select-post",
SELECT_COMMENT: "select-comment"
};
const reducer = (state, action) => {
switch (action.type) {
...,
case ActionTypes.ADD_POSTS: {
return {
...state,
timeline: {
...state.timeline,
posts: [...state.timeline.posts, ...action.payload]
}
};
}
case ActionTypes.SELECT_POST: {
return {
...state,
timeline: {
...state.timeline,
selectedPost: action.payload,
selectedComment: null
}
};
}
case ActionTypes.SELECT_COMMENT: {
return {
...state,
timeline: {
...state.timeline,
selectedComment: action.payload
}
};
}
...,
}
};
너는 아직 의식하지 못했겠지만timeline
조작은useEffect의 부작용 문제를 해결했다!기억하신다면, 원시 상하문 중 하나 SELECT_POST
가 있습니다. useEffect
가 변할 때 selectedComment
는 무효입니다.현재 우리는 selectedPost
과initialSelectedPost
를 설치할 수 있으며 initialSelectedComment
의 발사는 걱정하지 않아도 된다.테스트 목적으로만 사용되는useEffect
상태 요구 사항 제거
새로운 환경
마지막 난제는 React 상하문을 통해 응용 프로그램에 새로운 Reducer를 제공하는 것입니다.
const AppProvider = ({ initialState, reducer, children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
그래, 그럼 훨씬 깨끗해졌어.우리 팀은 Rails의 전체에서 일한다. 이것이 바로 내가 if
와 initialState
를 reducer
의 도구로 만들기로 결정한 이유이다.이 방법은 우리가 만들기로 결정한 모든 React 프로그램에 같은 공급자를 사용할 수 있도록 합니다.
결론
현재 이것은 내가 가장 좋아하는 React 응용 프로그램에서 전체 상태를 관리하는 방법이다. (이따가 블로그에서 추가 마법을 소개할 것이다.)
const TimelineContextProvider = ({ children }) => {
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(null)
const [selectedComment, setSelectedComment] = useState(null)
const state = { posts, selectedPost, selectedComment };
const actions = { setPosts, setSelectedPost, setSelectedComment }
useEffect(() => {
setSelectedComment(null)
}, [selectedPost])
return (
<TimelineContext.Provider value={{ state, actions}}>
{children}
</TimelineContext.Provider>
);
};
const TimelineContextProvider = ({
initialSelectedPost,
initialSelectedComment,
children
}) => {
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(initialSelectedPost);
const [selectedComment, setSelectedComment] = useState(
initialSelectedComment
);
const state = { posts, selectedPost, selectedComment };
const actions = { setPosts, setSelectedPost, setSelectedComment };
useEffect(() => {
if (initialSelectedPost != initialSelectedComment) {
setSelectedComment(null);
}
}, [selectedPost]);
return (
<TimelineContext.Provider value={{ state, actions }}>
{children}
</TimelineContext.Provider>
);
};
팀의 불평 중 하나는 "구성 요소에서 어떤 {X} 상하문을 사용해야 합니까?"이다.
AuthContext
와 TimelineContext
는 모두 전역 상태의 일부이기 때문에 하나의 해결 방안은 그것들을 조합하여state 대상 내의 영역을 분리하는 것이다.우리 이 문제를 해결하는 것부터 시작합시다.const AppContextProvider = ({
initialSelectedPost,
initialSelectedComment,
children
}) => {
const [user, setUser] = useState();
const [isLoggedIn, setIsLoggedIn] = useState();
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(initialSelectedPost);
const [selectedComment, setSelectedComment] = useState(
initialSelectedComment
);
const state = {
auth: { user, isLoggedIn },
timeline: { posts, selectedPost, selectedComment }
};
const actions = {
setUser,
setIsLoggedIn,
setPosts,
setSelectedPost,
setSelectedComment
};
useEffect(() => {
if (initialSelectedPost != initialSelectedComment) {
setSelectedComment(null);
}
}, [selectedPost]);
return (
<AppContext.Provider value={{ state, actions }}>
{children}
</AppContext.Provider>
);
};
내가 보기엔 큰 승리는 아니었지만, 지금은 팀이 더 즐거워졌다.광희의 부작용
React hooks와 1년 동안 합작한 후에 나는 어떤 환경에서useEffect
가 나쁜 생각일 수도 있다는 결론을 얻었다.(겸사겸사 한마디 하자면, 나는 네가 이 일을 한 예를 보고 싶다.
내가 얻은 더욱 구체적인 규칙은 우리의 응용 프로그램에서 전역 상태에 의존해서는 안 된다는 것이다. useEffect
나는 이것이 날카로운 칼로 너의 눈을 쉽게 찔러 뚫을 수 있다고 생각한다.그것은 하루하루 앞장서서 일하지 않는 사람들을 위해 프로젝트에서 일하는 데 장애를 증가시켰다.코드 라이브러리에서 일하는 사람들에게도 명심해야 할 일이다."{X} 을 변경하면 이 리셋이 실행될 것입니다. 수정해야 합니까?"
나의 해결 방안은 전역 상태에서 시종일관 (보통 95%) 사용하고 useReducer
전역 상태의 일부분에 의존하지 않는 것이다.
가자!
초기 상태
우선, 우리는 응용 프로그램의 초기 상태부터 시작할 것이다.
const initialState = {
auth: { user: null, isLoggedIn: false },
timeline: { posts: [], selectedPost: null, selectedComment: null }
};
그것은 매우 쉽다!우리의 초기 상태를 정의하면 우리의 모든 전 세계 상태를 한눈에 볼 수 있다.언제든지 전체 상태에 뭔가를 추가하고 싶을 때 useEffect
대상에 합리적인 기본값을 추가할 수 있습니다.예를 들어 initialState
는 처음에는 가짜였고 isLoggedIn
는 처음에는 공수조였다.
사랑하는 화생
Reducer 모드에서 내가 가장 좋아하는 부분은 Reducer의 모든 동작을 응용 프로그램과의 단일한 상호작용으로 볼 수 있다는 것이다.이러한 상호작용은 네트워크 요청이나 사용자 이벤트일 수 있습니다.동작을 설정할 때 "{X}가 발생할 때 상태가 어떻게 변합니까?"라고 묻습니다.그리고 정확한 유효 하중과 팔동작만 사용하면 이 조작을 수행할 수 있다.완성!현재 같은 상호작용이 두 곳에서 발생한다면 다른 구성 요소를 열고 논리를 기억할 필요가 없다.너는 행동만 발표하면 된다.
상하문posts
부분에 대해 우리는 두 가지 상호작용이 있는데 그것이 바로 로그인과 로그아웃이다.
코드 좀 봅시다.
const ActionTypes = {
SET_USER: "set-user",
LOGOUT_USER: "logout-user",
}
const reducer = (state, action) => {
switch (action.type) {
case ActionTypes.SET_USER: {
return {
...state,
auth: { ...state.auth, user: action.payload, isLoggedIn: true }
};
}
case ActionTypes.LOGOUT_USER: {
return {
...state,
auth: { ...state.auth, user: null, isLoggedIn: false }
};
}
...
}
};
와, 이거 K.I.s.s.:D야.
현재 우리는 호출auth
과 setUser
를 기억할 필요가 없다. 우리는 주어진 상호작용에 상응하는 조작을 할당하기만 하면 된다.
다음은 setIsLoggedIn
상태에 대한 조작을 추가합니다.
const ActionTypes = {
...,
ADD_POSTS: "add-posts",
SELECT_POST: "select-post",
SELECT_COMMENT: "select-comment"
};
const reducer = (state, action) => {
switch (action.type) {
...,
case ActionTypes.ADD_POSTS: {
return {
...state,
timeline: {
...state.timeline,
posts: [...state.timeline.posts, ...action.payload]
}
};
}
case ActionTypes.SELECT_POST: {
return {
...state,
timeline: {
...state.timeline,
selectedPost: action.payload,
selectedComment: null
}
};
}
case ActionTypes.SELECT_COMMENT: {
return {
...state,
timeline: {
...state.timeline,
selectedComment: action.payload
}
};
}
...,
}
};
너는 아직 의식하지 못했겠지만timeline
조작은useEffect의 부작용 문제를 해결했다!기억하신다면, 원시 상하문 중 하나 SELECT_POST
가 있습니다. useEffect
가 변할 때 selectedComment
는 무효입니다.현재 우리는 selectedPost
과initialSelectedPost
를 설치할 수 있으며 initialSelectedComment
의 발사는 걱정하지 않아도 된다.테스트 목적으로만 사용되는useEffect
상태 요구 사항 제거
새로운 환경
마지막 난제는 React 상하문을 통해 응용 프로그램에 새로운 Reducer를 제공하는 것입니다.
const AppProvider = ({ initialState, reducer, children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
그래, 그럼 훨씬 깨끗해졌어.우리 팀은 Rails의 전체에서 일한다. 이것이 바로 내가 if
와 initialState
를 reducer
의 도구로 만들기로 결정한 이유이다.이 방법은 우리가 만들기로 결정한 모든 React 프로그램에 같은 공급자를 사용할 수 있도록 합니다.
결론
현재 이것은 내가 가장 좋아하는 React 응용 프로그램에서 전체 상태를 관리하는 방법이다. (이따가 블로그에서 추가 마법을 소개할 것이다.)
const initialState = {
auth: { user: null, isLoggedIn: false },
timeline: { posts: [], selectedPost: null, selectedComment: null }
};
const ActionTypes = {
SET_USER: "set-user",
LOGOUT_USER: "logout-user",
}
const reducer = (state, action) => {
switch (action.type) {
case ActionTypes.SET_USER: {
return {
...state,
auth: { ...state.auth, user: action.payload, isLoggedIn: true }
};
}
case ActionTypes.LOGOUT_USER: {
return {
...state,
auth: { ...state.auth, user: null, isLoggedIn: false }
};
}
...
}
};
const ActionTypes = {
...,
ADD_POSTS: "add-posts",
SELECT_POST: "select-post",
SELECT_COMMENT: "select-comment"
};
const reducer = (state, action) => {
switch (action.type) {
...,
case ActionTypes.ADD_POSTS: {
return {
...state,
timeline: {
...state.timeline,
posts: [...state.timeline.posts, ...action.payload]
}
};
}
case ActionTypes.SELECT_POST: {
return {
...state,
timeline: {
...state.timeline,
selectedPost: action.payload,
selectedComment: null
}
};
}
case ActionTypes.SELECT_COMMENT: {
return {
...state,
timeline: {
...state.timeline,
selectedComment: action.payload
}
};
}
...,
}
};
const AppProvider = ({ initialState, reducer, children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
현재 이것은 내가 가장 좋아하는 React 응용 프로그램에서 전체 상태를 관리하는 방법이다. (이따가 블로그에서 추가 마법을 소개할 것이다.)
const initialState = {
auth: { user: null, isLoggedIn: false },
timeline: { posts: [], selectedPost: null, selectedComment: null }
};
const ActionTypes = {
SET_USER: "set-user",
LOGOUT_USER: "logout-user",
ADD_POSTS: "add-posts",
SELECT_POST: "select-post",
SELECT_COMMENT: "select-comment"
};
const reducer = (state, action) => {
switch (action.type) {
case ActionTypes.SET_USER: {
return {
...state,
auth: { ...state.auth, user: action.payload, isLoggedIn: true }
};
}
case ActionTypes.LOGOUT_USER: {
return {
...state,
auth: { ...state.auth, user: null, isLoggedIn: false }
};
}
case ActionTypes.ADD_POSTS: {
return {
...state,
timeline: {
...state.timeline,
posts: [...state.timeline.posts, ...action.payload]
}
};
}
case ActionTypes.SELECT_POST: {
return {
...state,
timeline: {
...state.timeline,
selectedPost: action.payload,
selectedComment: null
}
};
}
case ActionTypes.SELECT_COMMENT: {
return {
...state,
timeline: {
...state.timeline,
selectedComment: action.payload
}
};
}
default:
return state;
}
};
const AppProvider = ({ initialState, reducer, children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
너는 트위터에서 나의 랜덤 기술을 찾아서 잡담을 할 수 있다도구책
Shout out to Kent Dodds. He has some killer React patterns on his blog. Check it out.
The docs on AppProvider
from React
Reference
이 문제에 관하여(간단한 반응이 상하문에서 통제력을 잃을 때), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/basicbrogrammer/when-a-simple-react-context-gets-out-of-hand-28k
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(간단한 반응이 상하문에서 통제력을 잃을 때), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/basicbrogrammer/when-a-simple-react-context-gets-out-of-hand-28k텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)