0

我想创建一个自定义钩子,比如说useTextProcessor(initialText, props).
这是一种用于存储和操作文本(字符串)的反应状态。
useReducer用于制作累积状态。
代码是这样的:

interface TextEditorProps {
  disabled?: boolean
  onTextChanged?: (text: string) => void
}

const useTextProcessor = (initialText: string, props: TextEditorProps) => {
  const [state, dispatch] = useReducer(textReducerFunc, /*initialState: */{
    text  : initialText,
    props : props, // HACK: a dependency is injected here
  });
  state.props = props; // HACK: a dependency is updated here

  return [
    state.text,
    dispatch
  ] as const;
}

一种 hack 方法可以注入props以在textReducerFunc.
textReducerFunc是处理文本的主要功能(取决于动作类型和道具状态)。

我不知道如何以专业的反应方式props将依赖项插入。 如果我在内部声明, 是的,我可以访问,但是由于它是子函数,所以每次调用都会重新创建子函数。 这使得在下一次渲染时执行两次。 Ut 的制作也将执行两次。由于接受任何函数(可能是静态或内联函数) ,因此 包装不会产生任何影响。内联函数在每次渲染时总是通过引用不同。textReducerFunc
textReducerFuncuseTextProcessor(initialText, props)
propsuseTextProcessor
useReducertextReducerFunc
onTextChanged
useCallbackonTextChanged

作品详情如下textReducerFunc

interface TextState {
  props: TextEditorProps // holds my hack
  text: string // the actual data to be processed
}
interface TextAction {
  type: 'APPEND'|'UPPERCASE'
  payload?: string
}
const textReducerFunc = (state: TextState, action: TextAction) => {
  const props = state.props;
  if (props.disabled) return state; // disabled => no change

  switch(action.type) {
    case 'APPEND':
      const appendText = state.text + action.payload;
      props.onTextChanged?.(appendText); // notify the text has been modified
      return {...state, text: appendText};

    case 'UPPERCASE':
      const upperText = state.text.toUpperCase();
      props.onTextChanged?.(upperText); // notify the text has been modified
      return {...state, text: upperText};

    default:
      return state; // unknown type => no change
  } // switch
}

的用法useTextProcessor

export default function TxtEditor(props: TextEditorProps) {
  const [text, dispatch] = useTextProcessor('hello', props);

  return (
    <div>
      <div>
        {text}
      </div>
      <button onClick={() => dispatch({type: 'APPEND', payload: ' world'})}>append 'world'</button>
      <button onClick={() => dispatch({type: 'UPPERCASE'})}>uppercase</button>
    </div>
  )
}

你能建议我如何在没有任何黑客useReducer的情况下使用依赖吗?

这是我正在运行的沙箱

4

1 回答 1

1

我建议将状态提升到父组件。你需要一个“onTextChange”监听器。这告诉我状态位于错误的组件中。我建议将其放入父组件中。

type TextAction =
  | { type: 'APPEND'; payload?: string }
  | { type: 'UPPERCASE' }
  | { type: 'SET_DISABLED'; disabled: boolean };

interface TextState {
  text: string;
  disabled: boolean;
}

const textReducerFunc = (state: TextState, action: TextAction): TextState => {
  console.log(action);

  switch (action.type) {
    case 'APPEND': {
      if (state.disabled) return state;
      return { ...state, text: state.text + action.payload };
    }
    case 'UPPERCASE': {
      if (state.disabled) return state;
      return { ...state, text: state.text.toLocaleUpperCase() };
    }
    case 'SET_DISABLED':
      return { ...state, disabled: action.disabled };
  }
};

function useTextReducer() {
  return useReducer(textReducerFunc, {
    disabled: false,
    text: ''
  });
}

function TextBox({
  text,
  dispatch
}: {
  text: string;
  dispatch: Dispatch<TextAction>;
}) {
  return (
    <React.Fragment>
      <div>{text}</div>
      <button onClick={handleAppendPress}>append 'world'</button>
      <button onClick={handleUppercaseClick}>uppercase</button>
    </React.Fragment>
  );

  function handleAppendPress() {
    dispatch({ type: 'APPEND', payload: 'world' });
  }

  function handleUppercaseClick() {
    dispatch({ type: 'UPPERCASE' });
  }
}

function App() {
  const [state, dispatch] = useTextReducer();
  const { text } = state;

  useEffect(() => {
    console.log(`the text changed: ${text}`);
  }, [text]);

  return (
    <React.Fragment>
      <label>is disabled: </label>
      <input
        checked={state.disabled}
        type="checkbox"
        onChange={handleDisabledChange}
      />
      <TextBox dispatch={dispatch} text={state.text} />
    </React.Fragment>
  );

  function handleDisabledChange() {
    dispatch({ type: 'SET_DISABLED', disabled: !state.disabled });
  }
}

代码在这里

从我在您的代码中看到的(已经在其中的按钮),您可能希望将“禁用”复选框移动到TextBox组件中,进一步消除状态设置中的冗余。

于 2021-09-03T19:12:42.080 回答