실행 취소 및 아키텍처에 대한 영향

내가 개발 중인 제품은 철회와 재작업을 지원하지 않는다. 이것은 Signavio 에 떠도는 우스갯소리이다.비록 우리는 redux로 이 도구를 구축했지만, 우리는 여전히 이 기능을 실현하기 위해 노력하고 있다.나는 너를 귀찮게 하지 않을 것이다. 왜 이것은 우리에게 매우 어려운지 모르겠지만, 여러 해가 지나면서(!)나는 왜 기존의 응용 프로그램에 undo-redo 같은 기능을 넣는 것이 통상적으로 어려운지 알게 되었다.좋은 소식은 네가 그것을 더욱 쉽게 하기 위해 몇 가지 절차를 취할 수 있다고 믿는다는 것이다.

TL;박사


나는 react-undo-redo라는 도서관을 세웠다.GitHub에서 확인합니다.😄
그러나 이 글은 재작업을 취소하는 것이 아니라 구조에 관한 것이다.이 주제는 구조의 일부 선택이 어떻게 당신이 새로운 기능을 더욱 빨리 구축하는 데 도움을 줄 수 있는지를 보여주는 좋은 예이다.그 밖에 당신은 작은 결정이 얼마나 큰 영향을 미치는지 보게 될 것입니다.따라서 계속 공부:

  • The principles behind undo-redo ,

  • that your components should not care about how they get their data
  • that some concerns are better handled in isolation.
  • 실행 취소 및 재작업의 기초 지식


    상태 업데이트를 처리하는 프로그램을 상상해 봅시다.실행 취소나 재실행 개념이 없으면, 모든 업데이트는 현재 상태를 나타내는 새로운 상태를 만들 것입니다.만약 네가 우연히 만났다면, 너는 아마도 만났을 것이다 f(state, action) => state*.

    응용 프로그램에서 무슨 일이 발생할 때마다 우리는 새로운 현재 상태를 만들 것입니다.이렇게 함으로써 우리도 이전에 발생한 어떠한 정보도 소홀히 했다.행동을 철회하려면 과거와 미래를 추적해야 한다는 것이 분명하다.

    내가 알고 있는 undo-redo 구축의 가장 기본적인 실현은 현재 상태 옆에 두 개stacks를 도입하는 것이다.한 창고는 이전에 발생한 일을 추적하고, 다른 창고는 다음에 발생한 일을 추적한다.나는 창고를 사용하는 것을 좋아한다. 왜냐하면 그것들은 나의 심지 모형에 가장 적합하기 때문이다.이렇게 하면 우리는 과거에서 현재로, 미래로 들어간다.너도 그룹을 사용할 수 있고, 항상 끝에 추가할 수 있지만, 이것은 팝업 항목을 더욱 어렵게 할 것이다.
    상술한 데이터 구조가 있기 때문에, 우리는 그것을 어떻게 사용하는지 기본적인 규칙을 세워야 한다.

    추진 현황


    "취소"도 "복구"도 아닌 작업이 발생할 때마다 현재present를 과거로 옮기고 새로운present를 만들어야 합니다. 이것은 우리가 업데이트한 응용 프로그램 상태입니다.

    모든 사용자의 작업에 대해 우리는 future 창고를 삭제해야 한다.이것은 주로 우리가 심각한 두통에 걸리는 것을 피하기 위해서이다.만약 우리가 이렇게 하지 않는다면, 매번 다시 할 때, 우리는 우리가 어떤 가능한 미래를 선택할 것인지를 분명히 해야 한다.Back to the future을 본 사람마다 네가 그렇게 하고 싶지 않다는 것을 안다.
    겸사겸사 한마디 하자면, 나는 여기에서 Redux 조작이나 Reducer에 대해 이야기하는 것이 아니다.내가 본고에서 동작을 이야기할 때 내 말은'사용자가 뭔가를 했고 우리의 응용 프로그램 상태가 어떤 방식으로 업데이트되었다'는 것이다.이 점을 이해하는 것은 매우 중요하다. 속지 말고, 재작업을 취소하는 것은 Redux 기반 구조로만 구축할 수 있다고 생각한다.

    열다


    너는 우리가 동작을 취소할 때 무슨 일이 일어날지 이미 알아맞혔을 것이다.이런 상황이 발생하면, 우리는 과거 창고에서 가장 가까운 항목을 가져와 우리의 현재 항목으로 삼을 것이다.그 밖에 우리는 현재의'현재'를 미래 창고에 넣고 있다. (이것은 다시 하는 데 매우 중요하다.)

    다시 하다


    우리가 한 동작을 다시 할 때, 우리는 눈앞의 미래를 현재로 바꾸고, 지금은 과거로 바꾸었다.너는 아직도 나와 함께 있니?이것은 통상적으로 내가 방 안의 모든 사람이 보이지 않도록 확보하는 곳이다.

    지금 나는 이미 너를 헷갈리게 했다. 우리는 이 블로그 글의 실제 주제인 응용 프로그램 구조에 미친 영향에 대해 계속 토론하자.

    당신의 상태 접근이 당신에게 어떤 번거로움을 가져다 줄 수 있습니다


    이 절에서, 우리는 당신이 감속기와 동작을 사용하여 상태 처리를 하는 구조를 가지고 있다고 가정합니다.그 밖에 나는 고전적인'반'예시를 사용하여 그것을 이곳에서 간단하게 유지할 것이다. 비록 우리는 현실 세계가 훨씬 어려울 수 있다는 것을 알고 있지만.어떤 철회나 재구성 개념도 없는 상황에서 우리는 다음과 같은 코드를 제시했다.
    import React, { useReducer } from 'react'
    
    function counterReducer(state, action) {
      switch(action.type) {
        case 'increment': return state + 1
        case 'decrement': return state - 1
      }
    }
    
    function Counter() {
      const [count, dispatch] = useReducer(counterReducer, 0)
    
      return (
        <>
          Count: {count}
    
          <button onClick={() => dispatch({ type: 'decrement' })}>
            -
          <button>
    
          <button onClick={() => dispatch({ type: 'increment' })}>
            +
          <button>
        </>
      )
    }
    
    만약 우리가 실행 취소 기능을 응용 프로그램에 추가한다면 어떤 영향이 있습니까?많이 얘기할게요.우리는 자신에게 몇 가지 문제를 물어야 한다.
  • 우리는counterReducer알고 싶다past,presentfuture상태가 있습니까?
  • 만약 count 상태가 우리 응용 프로그램의 여러 위치에서 사용될 경우, 우리는 모든 소비자도 pastfuture 위치를 알고 싶습니까?
  • 만약 우리가 나중에 다른 방법을 사용하여 재작업을 취소하기로 결정한다면 어떤 영향을 미칠까요?
  • 개인적으로 마지막 문제는 나로 하여금 가장 많이 생각하게 한다.너는 아마 나의 cohesion에 관한 게시물과 different kinds of coupling에 관한 부분을 읽은 적이 있을 것이다.만약 우리가 응용 프로그램에 과거와 현재를 이해하기로 결정한다면, 이것은 전입 결합이 매우 큰 가치를 가진다는 것을 의미할 것이다.다시 말하면, undo-redo는 응용 프로그램의 모든 다른 내용과 결합될 것이다.이런 일은 나로 하여금 밤에 잠을 이루지 못하게 한다.이것은 듣기에 매우 옳지 않다.
    나는 이 화제가 왜 나를 괴롭히는지 한동안 생각했다.잠시 후, 나는 갑자기 생각났다.물론 이것은 재작업을 취소하는 것과 무관하다.나는 우리가 자주 범하는 오류 (적어도 어느 정도) 는 논리를 관리 상태에 너무 가깝게 옮기는 원어 (예를 들어 useReducer 라고 생각한다.
    네, 제가 설명할게요.
    그 이름을 감안하여 Counter 구성 요소는 계수만 관련되어야 한다.그러나 직접 사용useReducer을 통해 상태 관리를 매우 상세하게 처리한다.구성 요소'알기'상태는 감속기로 처리됩니다.이런 관점을 국가의 관리 방식과 결합시킴으로써 우리는 어떤 걱정도 바꾸기 어렵다.다행히도, every problem in software can be solved by adding a layer of indirection.
    import React, { useReducer } from 'react'
    
    function counterReducer(state, action) {
      switch(action.type) {
        case 'increment': return state + 1
        case 'decrement': return state -1
      }
    }
    
    function useCounter(initialCount) { 
      const [count, dispatch] = useReducer(counterReducer, initialCount) 
    
      return [count, dispatch]
    }
    
    function Counter() {
      const [count, dispatch] = useCounter(0)
      return (
        <>
          Count: {count}
    
          <button onClick={() => dispatch({ type: 'increment' })}>+<button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-<button>
        </>
      )
    }
    
    새로운 useCounter 갈고리는 구성 요소와 상태의 세부 사항을 결합시키는 추상적인 것을 만들었습니다.이러한 변경을 통해 우리는 구성 요소를 파괴하지 않고 상태와 감속기의 작업 방식을 변경할 수 있다.다시 말하면 Counter 구성 요소는 더 이상 관심을 가지지 않는다.그러나 감속기는 여전히 관심을 가지고 있다.많다

    걱정을 덜어주어 생활을 더욱 수월하게 하다


    마지막 절에서, 우리는 구성 요소 코드와 상태를 결합시킬 것이다.이런 방식을 통해 우리는 실행 취소 기능을 추가할 수 있으며, 구성 요소가 이 점을 알 필요가 없다.그러나 감속기는 약간의 조정이 필요하다.
    function counterReducer(state, action) {
      switch (action.type) {
        case "increment":
          return {
            past: [state.present, ...state.past], 
            present: state.present + 1, 
            future: [], 
          }
    
        case "decrement":
          return {
            past: [state.present, ...state.past], 
            present: state.present - 1, 
            future: [], 
          }
    
        case "undo": {
          const [present, ...past] = state.past
    
          return {
            past,
            present,
            future: [state.present, ...state.future],
          }
        }
    
        case "redo": {
          const [present, ...future] = state.future
    
          return {
            past: [state.present, ...state.past],
            present,
            future,
          }
        }
      }
    }
    
    만약 네가 나에게 묻는다면, 그 안의 비용은 매우 크다.incrementdecrement의 처리 절차를 보십시오.present위를 제외하고는 그것들은 같다.우리의reducer가 undo-redo의 특성을 알게 함으로써 우리는 앞으로 많은 추가 입력을 해야 할 것이다.물론 우리가 이 박문에서 최종적인 인식을 가지지 않으면.복원 프로그램도 undo-redo에 관심을 가질 필요가 없을 수도 있습니다.
    너의 감속기가 지금 살고 있다.그렇다면 그들은 왜 과거나 미래에 관심을 가져야 하는가?pastfuture는undo-redo만이 알아야 할 개념이다.그렇지 않으면, 우리가 undo-redo 주변에서 어떤 내용을 변경할 때마다, 우리는 모든 감축기를 변경해야 한다.
    function createUndoRedo(presentReducer) {
      return function undoRedoReducer(state, action) {
        switch (action.type) {
          case "undo": {
            const [present, ...past] = state.past
    
            return {
              past,
              present,
              future: [state.present, ...state.future],
            }
          }
          case "redo": {
            const [present, ...future] = state.future
    
            return {
              past: [state.present, ...state.past],
              present,
              future,
            }
          }
          default: {
            return {
              past: [state.present, ...state.past],
              present: presentReducer(state.present, action),
              future: [],
            }
          }
        }
      }
    }
    
    상술한 createUndoRedo 방법을 사용하면 우리는 다른 감속기를 강화하고 재작업 기능을 취소할 수 있다.이 모든 것들은 감속기(또는 감속기)가 이 기능을 이해하도록 할 필요가 없다.이것은 어떻게 우리의 응용 프로그램 코드를 바꿉니까?
    import React, { useReducer } from 'react'
    
    function counterReducer(state, action) {
      switch(action.type) {
        case 'increment': return state + 1
        case 'decrement': return state -1
      }
    }
    
    function useCounter(initialCount) {
      const [{ present: count }, dispatch] = useReducer(
        createUndoRedo(counterReducer), 
        { 
          past: [], 
          present: initialCount, 
          future: [] 
        }
      )
    
      return [count, dispatch]
    }
    
    function Counter() {
      const [count, dispatch] = useCounter(0)
    
      return (
        <>
          Count: {count}
    
          <button onClick={() => dispatch({ type: 'increment' })}>+<button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-<button>
        </>
      )
    }
    
    실행 취소 기능의 관심사를 추출함으로써 우리는 실현에 대한 영향을 제한할 수 있다.우리는 어떤 것들이 철회될 수 있기를 바란다. 이 개념은 useCounter 갈고리에 봉인되어 있다.결국 이 갈고리는 국가가 사는 곳과 그 형상을 정의했기 때문에 일리가 있다.이것은 우리가 사용하는 곳createUndoRedo이기 때문에 이 갈고리는 이 주의 present 부분도 합리적이다.중요한 것은 이런 추상을 통해 우리의 어떤 복원기와 구성 요소도 이 점을 알 필요가 없다는 것이다.
    내가 이 글을 쓸 때, 나는 이것이 매우 좋은 작은 프로젝트가 될 것이라고 생각한다.진입 단계: react-undo-redo, 오늘 사용할 수 있습니다.
    언제 추상을 사용하여 응용 프로그램을 더욱 잘 설계합니까?너는 내가 합리적인 결정을 내렸다고 생각하니, 아니면 네가 다른 방법을 취할 것이라고 생각하니?알려줘.

    좋은 웹페이지 즐겨찾기