Checkbox onCheck doesn't fire - javascript

I have a checkbox from material-ui that doesn't fire onCheck event.
<Checkbox
label="label"
onCheck={onCheck}
checked={currentDocument.ispublic}
/>
function onCheck() {
currentDocument.ispublic = !currentDocument.ispublic;
console.log("onCheck");
}
I tried removing checked property as described in this question: React Checkbox not sending onChange
currentDocument is an observable object stored in my store.

Can it be because based on https://material-ui-next.com/api/checkbox/#checkbox material-ui is exposing onChange not onCheck?

Without the full content, there is no way to test your code.
But there are some possible errors:
Where do onCheck and currentDocument come from? If it's in the component class, you need put this keyword.
Use arrow function for onCheck, otherwise, you can't access this without .bind(this)
The component class should have decorator #observer and currentDocument should have #observable
The code below is one possible answer, but I'm not sure what behavior you expected, so I didn't test. However, with MobX, the syntax is correct.
#observer
class MyCheckBox extends React.Component {
#observable currentDocument = {
ispublic: false,
};
render() {
return(
<Checkbox
label="label"
onCheck={this.onCheck}
checked={this.currentDocument.ispublic}
/>
);
}
onCheck = () => {
this.currentDocument.ispublic = !this.currentDocument.ispublic;
}
}

Related

Why is my React app completely reloading after a state change? [duplicate]

This question already has answers here:
Basic React form submit refreshes entire page
(4 answers)
Closed 3 years ago.
I am pretty new to React so apologies if this is a dumb question, which I suspect it is.
I have a simple React app with a dropdown, button and list. When the button is clicked, the selected item in the dropdown is added to the list. Each item added to the list also has a delete button associated with it.
I need the SelectComponent (dropdown and button) and ListComponent (list and buttons) to know what the items in the list are so they can add/remove items from it, so I am storing the state in the parent App component and passing it down to the children as props, along with a callback function that can update it (using setState()). Here is what I have:
Select Component
class SelectComponent extends Component<SelectProps, {}> {
constructor(props: any) {
super(props);
this.changeHandler = this.changeHandler.bind(this);
this.clickHandler = this.clickHandler.bind(this);
}
changeHandler(event: ChangeEvent<HTMLSelectElement>) {
currentSelection = event.target.value;
}
clickHandler(event: MouseEvent<HTMLButtonElement>) {
this.props.selectedItems.push(currentSelection);
this.props.updateList(this.props.selectedItems);
}
render() {
let optionItems = this.props.options.map((optionItem, index) =>
<option>{optionItem}</option>
);
return (
<form>
<div>
<select onChange={this.changeHandler}>
<option selected disabled hidden></option>
{optionItems}
</select>
<br />
<button type="submit" onClick={this.clickHandler}>Add to list</button>
</div>
</form>
);
}
}
List Component
class ListComponent extends Component<ListProps, {}> {
constructor(props: any) {
super(props);
this.removeListItem = this.removeListItem.bind(this);
}
removeListItem(i: number) {
this.props.selectedItems.filter((selection, j) => i !== j);
this.props.updateList(this.props.selectedItems);
}
render() {
let listItems;
if (this.props.selectedItems) {
listItems = this.props.selectedItems.map((listItem, index) =>
<li>{listItem}<button onClick={() => this.removeListItem(index)}>Delete</button></li>
);
}
return (
<div>
<ul>
{listItems}
</ul>
</div>
);
}
}
main App
class App extends Component<{}, State> {
constructor(props: any) {
super(props);
this.state = {
selectedItems: []
}
this.updateList = this.updateList.bind(this);
}
updateList(selectedItems: string[]) {
this.setState({selectedItems});
}
render() {
return (
<div>
<SelectComponent options={["Cyan", "Magenta", "Yellow", "Black"]} selectedItems={this.state.selectedItems} updateList={this.updateList} />
<ListComponent selectedItems={this.state.selectedItems} updateList={this.updateList} />
</div>
);
}
}
I also have a couple interfaces defining the props and state as well as a variable to hold the currently selected item in the dropdown.
What I want to happen is: the "Add to list" button is pressed, adding the current dropdown selection to the props, then passing the props to the updateList() function in the parent class, updating the state. The parent class should then re-render itself and the child components according to the new state. From what I can tell by looking at the console, this does happen.
However for some reason after it gets done rendering the ListComponent, the app completely reloads, clearing the state and the list and returning the dropdown to it's default value. I can tell because I see Navigated to http://localhost:3000/? in the console right after the ListComponent render function is called.
So what have I done wrong? Again I am pretty new to React so I have a feeling this is something simple I am missing. Any help would be greatly appreciated!
Edit: Forgot to mention (although it is probably obvious) that I am coding this in TypeScript, although I don't think that is related the issue.
If you are working with form and submit handler then you have to set the event.preventDefault() in your submit method.
You have to set preventDefault() in clickHandler method.
clickHandler(event) {
event.preventDefault(); // set it...
this.props.selectedItems.push(currentSelection);
this.props.updateList(this.props.selectedItems);
}
because you're using form and button type submit. Type submit is reason your app reload.
To prevent app reload by default, in the clickHandler function of Select Component, add preventDefault in the top of function
event.preventDefault();

Propogating Events to Child Components in React

I want to send events down to my React child.
I feel like this is kind of an easy thing to do, so maybe i just have a mental block, and there is something obvious that is staring me in the face.
Anyway, I have a little Test app which illustrates the problem:
export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
let {buttonClicked, textFieldChanged} = this.state
return (
<div>
<button onClick={()=>this.handleClick()}>
Click
</button>
<input type={"text"} onChange={()=>this.handleTextChange()}/>
<Inner buttonClicked={buttonClicked} textFieldChanged={textFieldChanged}/>
</div>
);
}
handleClick(e) {
this.setState({ buttonClicked: true })
}
handleTextChange(e) {
this.setState({textFieldChanged:true})
}
}
class Inner extends React.Component {
render() {
let {buttonClicked, textFieldChanged} = this.props;
return (
<React.Fragment>
<div>Clicked : {buttonClicked ? "CLICKED!" : " "}</div>
<div>Text input : {textFieldChanged ? "TYPED!" : " "}</div>
</React.Fragment>
);
}
}
A button and a textfield live in the parent. Both these widgets can fire off events and change the child component.
This is simply achieved by passing a state value as a property down to the child. Very easy stuff.
However I would like an either/or situation. When I click the button this removes the text event, and vice versa. Ie. I do not want to see a situation like this :
Now there is a very obvious way to fix this by changing the state value to "false" of the other value.
handleClick(e) {
this.setState({ buttonClicked: true, textFieldChanged: false })
}
handleTextChange(e) {
this.setState({textFieldChanged:true, buttonClicked: false})
}
Is there any OTHER way of doing this?
The problem is that I have LOTS and LOTS of even handlers in my component and I don't want to negate the other state properties of the other values.
if i understood you correctly just one function will help - pass the attribute name into it
handleClick(propName) {
this.setState({
...this.state,
[propName]: !this.state[propName]
})
}
Create property lastEventType in parent component state , whenever you click or type - update it. And pass only this property to Inner component

Input text inside a react component

I'm trying to create an input text inside a react component and then I realised that it's a bad praxis. So I investigated a little bit so I found Controlled-Components, so I think this is what I need, but looking at my Component I do not know how to create it.
I do not have an extends Redux.Component so a friend suggested me to create a Component but couldn't get succeed.
What I was trying is this :
Inside my component
<input
...
/>
{" "}
<input
...
/>
<span>
<myButton
...
arguments={[document.getElementById("id1").value, document.getElementById("id2").value]}
>
[ send ]
</myButton>{" "}
</span>
But I'm getting this error :
The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!
EDIT
On my component where I have all of those code I have this :
<myButton
id={id}
arguments={[intputStuff]}
>
So my problem is if I do what Tom's says I do not have the id in the other component.
So the thing should be create this component inside the other component and then get the values of the inputtexts and put them as an arguments
It's not clear from your post what exactly you're trying to accomplish.
It appears that you're trying to build a component with 2 text inputs and a button.
If you want the button to "submit" the values of the two inputs, you should do something like this:
class SomeComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
value1: props.initialValue1,
value2: props.initialValue2
}
}
onChangeText = (event) => this.setState({ [event.target.name]: event.target.value })
onClickSubmit = (event) => {
let { onSubmit } = this.props
if(typeof onSubmit !== 'function') return
let { value1, value2 } = this.state
return onSubmit([ value1, value2 ])
}
render() {
let {
initialValue1,
initialValue2,
onSubmit,
className,
...props
} = this.props
let {
value1,
value2
} = this.state
return (
<div className={`SomeComponent ${className}`} {...props}>
<input value={value1} name="value1" onChange={this.onChangeText} />
<input value={value2} name="value2" onChange={this.onChangeText} />
<button onClick={this.onClickSubmit}>
Submit
</button>
</div>
)
}
}
A few notes:
This example uses a bunch of futuristic JS: destructuring, rest/spread, class properties, computed property names, and arrow functions. Each feature is being leveraged for a specific purpose, not just because they're cool. If your environment doesn't support some of these features, you'll need to find a workaround that makes good on some additional constraints.
This is not a controlled component, but it does contain 2 controlled inputs. It uses the "initialValue" pattern: the owning component provides starting values, but is unaware of the blow-by-blow as the user types each character. The owning component is only notified of the new values when the button is clicked. This pattern can result in loss of data if the owner is re-rendered before the current value are submitted.
Generally, when using React, you want to avoid using native DOM methods to access or manipulate elements. (There are plenty of exceptions, of course.) One reason you want to avoid native DOM methods is that component lifecycle methods might execute before the React renderer has actually updated the DOM -- so document.getElementById('someid') might return undefined.

React Executing a method inside a component

I have a component like this:
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props, context) {
super(props, context);
this.state = {
isActive: false,
}
}
showMyComponent() {
this.setState({
isActive: true,
});
}
hideMyComponent() {
this.setState({
isActive: false,
});
}
render() {
return (
<div>
<h1>Compoent Here</h1>
</div>
);
}
}
export default MyComponent;
Now, on my index.js I am adding several components.
...
<Header />
<Nave />
Can I now do something like this here:
MyComponent.showMyComponent();
Like you normally call a function?
If not, how is this done?
You can use references. In your render() method you can get the ref. e.g.
<MyComponent ref={ref => {this.myComponent = ref}}/>
You need to create a field myComponent and assign it to it. With that you can call it like this.myComponent.showMyComponent()
See here Refs and the DOM
Use State
You are thinking about react wrong. You should not have to call a components function like this ever.
You can pass a prop to the component that will make the component hide or show.
or wrap the component in a if in the parent. Use the parents state to hide or show the component.
Like
if (someCondition) {
<MyComponent />
}
It's doable, even if some people hates this option, cause it's not the official React way, true.
You can define any public method on your component classes (such as a reset method on a Typeahead) and call those public methods through refs (such as this.refs.myTypeahead.reset()). In most cases, it's clearer to use the built-in React data flow instead of using refs imperatively.
But However, thinking out of the box, is not forbidden so you can use refs for this.
class Parent extends Component {
onSomeThing() {
// Call some method of myChild
this.myChild.myChildsPublicMethod()
}
render() {
return <MyChild ref={ref => { this.myChild = ref; }} />
}
}
// MyChild
// Just as demo using Pure components here.
// You could use the normal class notation..
const MyChild = () => <div>Ola</div>;
MyChild.someMethod = () => console.log('Ola');
More here https://zhenyong.github.io/react/docs/more-about-refs.html

React: Controlling input value with both props and state

Given a React component with a controlled input, I would like to be able to:
Set the value of the input from the parent's state
Allow the user to change the input to any value
Update the parent's state only after the user submits and input passes validation.
I can accomplish 1 and 2 with the snippet below, but since the value came into the ChildComponent via props, I'm not sure how I can change the input value without changing the value of myInput on the parent.
class ChildComponent extends React.Component
{
render(){
return <input type="text" value={this.props.inputValue} onChange={this.handleChange.bind(this)} />
}
handleChange(e){
this.props.onInputChange(e.target.value);
}
handleSubmit(){
// do some validation here, it it passes...
this.props.handleSubmit();
}
}
class ParentComponent extends React.Component{
constructor(){
super();
this.state = {myInput: ""};
}
render(){
return <ChildComponent inputValue={this.state.myInput} onInputChange={this.handleChange.bind(this)} />
}
handleChange(newValue){
this.setState({myInput: newValue});
}
handleSubmit(){
// do something on the server
}
}
Then you just need to move the state to the child component, instead of rendering from props.inputValue directly. Basically you'd just move handleChange to the child.
Set the initial value from props.inputValue in getInitialState, then make sure to update the child state in componentWillReceiveProps.
componentWillReceiveProps is deprecated
Source: https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
This lifecycle was previously named componentWillReceiveProps. That
name will continue to work until version 17. Use the
rename-unsafe-lifecycles codemod to automatically update your
components.
Use something like this instead:
componentDidUpdate(prevProps) {
if (this.props.yourObj != null && prevProps.yourObj !== this.props.yourObj) {
this.setState({
yourStateObj = this.props.yourObj
});
}
}

Categories