如果从 props 传递,则处理 useReducer

handle useReducer if pass from props

我正在尝试针对特定用例在组件中对 useReducer 进行可覆盖的使用。

下面是我的overfly简化工作状态:

function defaultToggleReducer (state, action) {
  console.log('Default toggler used')
  return !state
}

function customTogglerReducer (state, action) {
  console.log('Custom toggler used')
  return !state
}

// Dummy wrap to log the initialization from `Togller` component to point the twice initializations
function toggleInitializer (state) {
  console.log('toggleInitializer')
  return state
}

function Toggler (props) {
  const [
    toggleState,
    toggleDispatch,
  ] = React.useReducer(defaultToggleReducer, false, toggleInitializer)
  
  // Here is the prt making the previous `useReducer` useless
  const state = props.toggleState !== undefined ? props.toggleState : toggleState
  const dispatch = props.toggleDispatch || toggleDispatch
  
  return (
    <button
      type="button"
      onClick={() => dispatch({ type: 'add' })}>
      {Boolean(state).toString()}
    </button>
  )
}

function App () {
  const [customToggleState, customToggleDispatch] = React.useReducer(
    customTogglerReducer,
    false
  )

  return (
    <div>
      <fieldset>
        <legend>Default</legend>
        <Toggler />
      </fieldset>
      <fieldset>
        <legend>Customized</legend>
        <Toggler
          toggleState={customToggleState}
          toggleDispatch={customToggleDispatch} />
        <button
          type="button"
          onClick={() => customToggleDispatch({ type: 'parentAction' })}>
          This is why I want dispatch
        </button>
      </fieldset>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root" />

这里有一些难闻的东西,我毫不怀疑这是有效的,但即使没有使用,useReducer 中调用 Toggler 的事实也让我怀疑。所以,我认为这是一个死的减速器。

所以我的问题是:

你有没有办法从父组件正确地实现 reduce 控制(至少:比这更好)?

使用组件的组合来解决这个问题。将 Toggler 分成两个组件 - 需要外部减速器的哑 Toggler 和呈现 Toggler 并提供减速器的 DefaultToggler

如果您需要标准切换器,请使用 DefaultToggler,如果您需要自定义版本,请使用哑 Toggler,并提供自定义缩减器。

function defaultToggleReducer (state, action) {
  console.log('Default toggler used')
  return !state
}

function customTogglerReducer (state, action) {
  console.log('Custom toggler used')
  return !state
}

// Dummy wrap to log the initialization from `Togller` component to point the twice initializations
function toggleInitializer (state) {
  console.log('toggleInitializer')
  return state
}

function Toggler ({ toggleState: state, toggleDispatch: dispatch }) {  
  return (
    <button
      type="button"
      onClick={() => dispatch({ type: 'add' })}>
      {Boolean(state).toString()}
    </button>
  )
}

function DefaultToggler () {
  const [
    toggleState,
    toggleDispatch,
  ] = React.useReducer(defaultToggleReducer, false, toggleInitializer)

  return (
    <Toggler 
      toggleState={toggleState} 
      toggleDispatch={toggleDispatch} 
      />
  );
};

function App () {
  const [customToggleState, customToggleDispatch] = React.useReducer(
    customTogglerReducer,
    false
  )

  return (
    <div>
      <fieldset>
        <legend>Default</legend>
        <DefaultToggler />
      </fieldset>
      <fieldset>
        <legend>Customized</legend>
        <Toggler
          toggleState={customToggleState}
          toggleDispatch={customToggleDispatch} />
        <button
          type="button"
          onClick={() => customToggleDispatch({ type: 'parentAction' })}>
          This is why I want dispatch
        </button>
      </fieldset>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root" />