Reputation: 2692
I have a custom input component that has an edit mode, I use multiple inputs and my goal is to extract all the state from the input and put it on the parent.
I managed to do it for the input value since I create a useState for every input, but how can I achieve the same thing for the toggle behavior. I want to have a button next to each input that toggles the state of the input.(Show/Hide the input).
Right now when I press edit all the edits Booleans get triggered so all the inputs become visible.
How can I toggle one input at a time from the parent, Basically I want my custom components to be purely for presentation and the state should live on the parent
Example:
const Parent = () => {
const [value1, setValue1] = useState('')
const [value2, setValue2] = useState('')
const [value3, setValue3] = useState('')
const [editMode, setEditMode] = useState(false)
return (
<div>
<ChildCustomInput value={value1} updateValue={setValue1} editMode={editMode} updateEditMode={setEditMode} />
<ChildCustomInput value={value2} updateValue={setValue2} editMode={editMode} updateEditMode={setEditMode}/>
<ChildCustomInput value={value3} updateValue={setValue3} editMode={editMode} updateEditMode={setEditMode}/>
</div>
)
}
const ChildCustomInput = (props)=> {
// ref to update the input's value in the parent
const textInputEl = useRef(null)
// method to update the parent's values
const updateComponentValue = () => {
props.updateValue(textInputEl.current.value)
props.updateEditMode(!props.editMode)
}
// Toggle Edit mode
const changeEditMode = () => {
props.updateEditMode(!props.editMode)
}
const renderEditView = () => {
return (
<div>
<input ref={textInputEl}></input>
<button onClick={updateComponentValue}>
OK
</button>
</div>
)
}
const renderDefaultView = () => {
return (
<div>
<div>
{props.value}
</div>
<button onClick={changeEditMode}>
EDIT
</button>
</div>
)
}
return (
<div>
{
props.editMode
? renderEditView()
: renderDefaultView()
}
</div>
)
}
export default Parent;
Upvotes: 2
Views: 174
Reputation: 1817
For this case you should have a separated state that manages the toggle property for each input. Like:
const [value1EditMode, setValue1EditMode] = useState(false);
const [value2EditMode, setValue2EditMode] = useState(false);
And so on for each input.
But I suggest you to use the useReducer hook, so you just manage the whole child components state with just one reducer. You could do something like this:
const initialState = {
input1: {
value: "",
editMode: false
},
input2: {
value: "",
editMode: false
},
input3: {
value: "",
editMode: false
}
};
function reducer(state, action) {
switch (action.type) {
case "CHANGE_VALUE":
let newState = {
...state
};
newState[action.key].value = action.value;
return newState;
case "SET_EDIT_MODE":
newState = {
...state
};
newState[action.key].editMode = action.editMode;
return newState;
default:
throw new Error();
}
}
const Parent = () => {
const [state, dispatch] = useReducer(reducer, initialState);
function updateValue(key, value) {
dispatch({ type: "CHANGE_VALUE", value, key });
}
function updateEditMode(key, editMode) {
dispatch({ type: "SET_EDIT_MODE", editMode, key });
}
return (
<div>
<ChildCustomInput
value={state.input1.value}
updateValue={value => {
updateValue("input1", value);
}}
editMode={state.input1.editMode}
updateEditMode={editMode => {
updateEditMode("input1", editMode);
}}
/>
<ChildCustomInput
value={state.input2.value}
updateValue={value => {
updateValue("input2", value);
}}
editMode={state.input2.editMode}
updateEditMode={editMode => {
updateEditMode("input2", editMode);
}}
/>
<ChildCustomInput
value={state.input3.value}
updateValue={value => {
updateValue("input3", value);
}}
editMode={state.input3.editMode}
updateEditMode={editMode => {
updateEditMode("input3", editMode);
}}
/>
</div>
);
};
Here is a working example: https://codesandbox.io/s/eloquent-hellman-8on6s?file=/src/Parent.js:52-1818
Upvotes: 0
Reputation: 3724
It seems that you need a editMode boolean in the parent state for each of your input (ie editMode1, editMode2, editMode3), and each ChildCutomInput should use its own editModeX and setEditModeX instead of sharing the same boolean.
const Parent = () => {
const [value1, setValue1] = useState('');
const [value2, setValue2] = useState('');
const [value3, setValue3] = useState('');
const [editMode1, setEditMode1] = useState(false);
const [editMode2, setEditMode2] = useState(false);
const [editMode3, setEditMode3] = useState(false);
return (
<div>
<ChildCustomInput value={value1} updateValue={setValue1} editMode={editMode1} updateEditMode={setEditMode1} />
<ChildCustomInput value={value2} updateValue={setValue2} editMode={editMode2} updateEditMode={setEditMode2}/>
<ChildCustomInput value={value3} updateValue={setValue3} editMode={editMode3} updateEditMode={setEditMode3}/>
</div>
)
}
Upvotes: 1