Reputation: 847
I'm trying to make an overridable usage of a useReducer
in a Component for specific use cases.
Following my overfly simplified working state of work :
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" />
Here, there is something smell bad, I have no doubts about this is working but the fact where useReducer
is called in Toggler
even if not used let me suspicious. So, it's a dead reducer I think.
So my question is:
Did you have a way to achieve a reduce
control from parent component properly (at least: better than this) ?
Upvotes: 1
Views: 1253
Reputation: 191976
Use components' composition to solve this problem. Break Toggler
to two components - dumb Toggler
that expects an outside reducer, and DefaultToggler
that renders Toggler
, and supplies the reducer.
If you need a standard toggler, use DefaultToggler
, and if you need a custom version use the dumb Toggler
, and supply a custom reducer.
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" />
Upvotes: 1