I've a isView and setIsView in the ParentComponent and passing them down to the ChildComponent as props and trying to do show/hide conditional rendering but setIsView seems not to be working and isView value in the props remains same.
const ParentComponent = props => {
const [isView, setIsView] = useState(true);
const onChange = selectedOption => {
selectedOption === 'Report'
? setIsView(true)
: setIsView(false);
};
return (
<div>
<ChildComponent
isView={isView}
onChange={onChange}
/>
</div>
);
};
const ChildComponent = props => {
const {isView, onChange} = props;
return (
<div>
<RadioButton
onChange={() => onChange('Not-Report')}
/>
<If condition={isView}>
<ChildComponent2>
</If>
</div>
);
};
Edit: changed onChange={onChange('Not-Report')} to onChange={() => onChange('Not-Report')} as suggested by some. still not working.
Try feeding the onChange method as a callback function instead.
const ChildComponent = props => {
const {isView, onChange} = props;
return (
<div>
<RadioButton
onChange={() => onChange('Not-Report')} // <- Here
/>
<If condition={isView}>
<ChildComponent2>
</If>
</div>
);
};
Update child component onChange function as follows:
<RadioButton
onChange={() => onChange('Not-Report')}
/>
If you pass onChange only, it will be regarded with the function that has event as a parameter rather than the prop's onChange function.
To make it work like your way,
const ChildComponent = ({isView, onChange}) => {
const onRadioChange = () => {
onChange('Not-Report')}
}
return (
<div>
<RadioButton
onChange={onRadioChange}
/>
<If condition={isView}>
<ChildComponent2>
</If>
</div>
);
};
i am wrote this code
ParentComponent
const ParentComponent = (props) => {
const [isView, setIsView] = useState(true);
const onChange = (selectedOption) => {
console.log("selectedOption = ", selectedOption);
selectedOption === "Report" ? setIsView(true) : setIsView(false);
};
return (
<div>
<ChildComponent isView={isView} onChange={onChange} />
</div>
);
};
ChildComponent
const ChildComponent = (props) => {
const { isView, onChange } = props;
return (
<div>
<input
type="radio"
checked={isView}
onClick={() => {
onChange("Not-Report");
}}
/>
isView = {isView ? "true" : "false"}
</div>
);
};
i change onChange to onClick and use checked
Work Demo
Related
How to keep input focus when changing state inside onFocus event
seems like it's not happening with simple input
only when using customInput
const CustomInput = ({ onFocus, state }) => {
return (
<div>
<input onFocus={onFocus} />
</div>
);
};
const TestInput = () => {
const [state, setState] = React.useState(false);
return (
<FilledInput
inputComponent={(props) => {
return (
<CustomInput onFocus={props.onFocus} state={state} />
);
}}
onFocus={() => {
setState(true);
}}
/>
);
};
I have an array of fragments that I'm passing into ChildComponent. The fragments must have onChange attributes set to a handler that exists inside the ChildComponent, but as they are written outside of it doing so breaks the app. I can't define the fragments inside ChildComponent and I can't define ChildComponent inside ParentComponent. How do I do this properly?
const fragments = [
const Screen1 = () => {
return (
<>
<input type="text" id="screen1_input1" onChange={onChangeHandler} />
</>
)
};
const Screen2 = () => {
return (
<>
<input type="text" id="screen2_input1" onChange={onChangeHandler} />
</>
)
};
]
ChildComponent.js
const ChildComponent = (props) => {
let index = props.index
const fragments = props.fragments
const onChange = (e) => {
//whatever
}
return (
<>
<h2 className="screens">
{fragments[index]()}
</h2>
</>
)
}
ParentComponent.js
import ChildComponent from './ChildComponent'
const ParentComponent = (props) => {
let index = 3
return (
<ChildComponent index='3'/>
)
}
You can convert fragments array into a function which takes onChangHandler and return an array itself.
Below is the refactored code, for simplicity I'm just logging the input element id and the value that's being inputted.
const { Fragment } = React;
const fragments = (onChangeHandler) =>
[
<input type="text" id="screen1_input1" onChange={onChangeHandler} />,
<input type="text" id="screen2_input1" onChange={onChangeHandler} />
];
const ChildComponent = ({ index, fragments }) => {
const onChange = e => {
const { target: {id, value} } = e;
console.log(id, value);
};
return (
<Fragment>
<h2 className="screens">{fragments(onChange)[index]}</h2>
</Fragment>
);
};
const ParentComponent = props => {
return <ChildComponent index={1} fragments={fragments}/>;
};
ReactDOM.render(<ParentComponent />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Define fragments like this
const fragments = [
const Screen1 = (onChangeHandler) => {
return (
<>
<input type="text" id="screen1_input1" onChange={onChangeHandler} />
</>
)
};
const Screen2 = (onChangeHandler) => {
return (
<>
<input type="text" id="screen2_input1" onChange={onChangeHandler} />
</>
)
};
]
ChildComponent.js
const ChildComponent = (props) => {
let index = props.index
const fragments = props.fragments
const onChange = (e) => {
//whatever
}
return (
<>
<h2 className="screens">
{fragments[index](onChange)}
</h2>
</>
)
}
Hello i have an component where i set a prop darkMode in a useState. I want to give this to the child component with onDarkmodeChange={darkMode}. This child gave this prop also to his child. In the toggle switch i want to use the prop onDarkmodeChange. But how am i supposed to use this? I tink i am doing something wrong because darkMode is not updated anymore.
export function PageLayout({ children }) {
const [darkMode, setDarkMode] = React.useState(false)
return (
<Header onDarkModeChange={darkMode} />
)
}
export function Header({ onDarkModeChange }) {
return (
<ToggleSwitch {... { onDarkModeChange }} />
)
}
export function ToggleSwitch({ onDarkModeChange }) {
const [isToggled, setIsToggled] = React.useState(onDarkModeChange)
return (
<label className={styles.component}>
<input className={styles.input} type='checkbox' checked={isToggled} onChange={() => setIsToggled(!isToggled)} />
<span className={cx(styles.switch, isToggled && styles.isActive)} />
</label>
)
}
I think it's not necessary to have the isToggled state since it's going to have the same value that the darkMode state. Also, I would move all the logic to the parent so that both children can access and manipulate that state, in case that it's necessary. Try this:
export function PageLayout({ children }) {
const [darkMode, setDarkMode] = React.useState(false)
const switchDarkMode = () => {
setDarkMode(currentDarkMode => !currentDarkMode)
}
return (
<Header darkMode={darkMode} switchDarkMode={switchDarkMode} />
)
}
export function Header({ darkMode, switchDarkMode }) {
return (
<ToggleSwitch darkMode={darkMode} switchDarkMode={switchDarkMode} />
)
}
export function ToggleSwitch({ darkMode, switchDarkMode }) {
return (
<label className={styles.component}>
<input className={styles.input} type='checkbox' checked={darkMode} onChange={switchDarkMode} />
<span className={cx(styles.switch, darkMode && styles.isActive)} />
</label>
)
}
const shoopingList = [{name:'some thing', id:1},{name:'some string', id:4}]
const CurrentLists = ({ shoppingList }) => {
const arr = [...shoppingList]
arr.map((item, index) => {
item.isChecked = false
})
const [checkedItems, setCheckeditems] = useState(arr)
const handleOnChange = (e) => {
const index = e.target.name
const val = e.target.checked
checkedItems[index].isChecked = e.target.checked
setCheckeditems([...checkedItems])
}
return (
<div>
{checkedItems.map((item, index) => {
console.log('item check', item.isChecked)
return (
<CheckBox
key={index}
name={index}
checked={item.isChecked}
text={item.name}
onChange={handleOnChange}
/>
)
})}
</div>
)
}
const CheckBox = ({ checked, onChange, text, className = '', name }) => {
let css = classnames({
activebox: checked,
})
return (
<div className={'CheckBoxComponent ' + className}>
<div className={'checkbox ' + css}>
<input
name={name}
type="checkbox"
onChange={onChange}
/>
{checked && <i className="far fa-check signcheck" />}
</div>
<label>{text}</label>
</div>
)
}
I got some checkboxes. when I click the checkbox, my component doesn't re-render. What's wrong here? I might be using the hook setState wrong.
On every re-render you are basically setting isChecked property to false. Try updating your component like this:
const CurrentLists = ({ shoppingList }) => {
const [checkedItems, setCheckeditems] = useState(shoppingList)
const handleOnChange = useCallback(
(e) => {
const index = e.target.name
let items = [...checkedItems];
items[index].isChecked = e.target.checked;
setCheckeditems(items);
}, [checkedItems]
);
return (
<div>
{checkedItems.map((item, index) => {
console.log('item check', item.isChecked)
return (
<CheckBox
key={index}
name={index}
checked={item.isChecked}
text={item.name}
onChange={handleOnChange}
/>
)
})}
</div>
)
}
You may also notice usage of useCallback. It ensures that your callback is memoized and not created on every re-render - more about it.
In handleOnChange you are mutating the state directly, and because the state reference is not changed React does not re-render. To fix this change the line setCheckeditems(checkedItems) to setCheckeditems([...checkedItems]).
Also in your render, you are rendering shoppingList, but what you need to render is checkedItems
Editing my question to make it a bit clearer
I don’t want the button to re-rendering when I type in the field and when I click on the button I want to update a state object
Here I have 2 components
const mainState = {
title: '',
};
const ButtonComponent = ({ confirmTitleName }) => {
return (
<>
<TestReRender label={'Button Container'}/>
<button style={{backgroundColor: 'red', outline: 'none'}} onClick={() => confirmTitleName('confirmTitleName >>>')}>CLICK ME</button>
</>
)
};
const InputComponent = ({ state, setState }) => {
return (
<>
<TestReRender label={'Input Container'}/>
<input
type="text"
value={state}
onChange={(e) => setState(e.target.value)}
/>
</>
)
};
Then I have created a component made up of the previous two
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
const Btn = () => <ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
return (
<>
<InputComponent state={state} setState={setState} />
<Btn />
</>
)
});
The last component InputAndButtonComponent is then imported in the Main component
const Main = () => {
const [confirmTitle, setConfirmTitle] = useState(mainState);
const confirmTitleName = useCallback((value) => {
setConfirmTitle((prevState) => (
{
...prevState,
title: value
}
))
}, []);
return (
<main className={styles.CreateWorkoutContainer}>
<>
<TestReRender label={'Main Container'}/>
<div>
<InputAndButtonComponent confirmTitleName={confirmTitleName} />
</div>
</>
</main>
)
};
Now the problem is that when I write the component InputAndButtonComponent as follow it re-renders when I type in the input field
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
return (
<>
<InputComponent state={state} setState={setState} />
// This re-renders when typing
<ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
</>
)
});
But the original version does not re-render when I type in the field
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
// This makes the <Btn /> below not re-rendering. I don't understand why
const Btn = () => <ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
return (
<>
<InputComponent state={state} setState={setState} />
// This does not re-render
<Btn />
</>
)
});