onclick props not working with Material UI? - javascript

here is my situation on React.js
I have a function on my App.js call selectNumberOfPeople,
then In my child component ( call General) I had a button as:
<button className="selectedNumberOfPeopleButton" onClick={this.selectedNumberOfPeople} value="1">
1
</button>
which was displaying the value in the console on click.
Works perfectly.
I want to use a button from Material UI instead now, so I have replace my button with:
<RaisedButton className="selectedNumberOfPeopleButton"
onClick={this.props.selectedNumberOfPeople}
value="1"
label="1"
labelPosition="before"
primary={true}
/>
But the value doesnt display anymore int he console when using this . . .
though the function being in the parent component I do pass it by:
<General selectNumberOfPeople={this.selectNumberOfPeople} selectedPanel={this.showPanelAndHideOthers} />
and I tried to updated my child component ( General.js) like:
<RaisedButton selectNumberOfPeople={this.props.selectNumberOfPeople}
className="selectedNumberOfPeopleButton"
onClick={this.props.selectedNumberOfPeople}
value="1"
label="1"
labelPosition="before"
primary={true}
/>
but it's still not working....
For your information,
the selectNumberOfPeople is in App.js as
selectNumberOfPeople(value) {
console.log('select number of people');
// update the state and provide a callback handler to be called after the state is updated.
// referencing the state before the call back function
// () => {
// console.log(this.state.numberOfPeople)
// })
// will leave the state with the same value as before the setState function is called.
this.setState(
{
numberOfPeople: value,
},
() => {
console.log(this.state.numberOfPeople);
}
);
}
and in my general.js (child component)
selectedNumberOfPeople(e) {
this.props.selectNumberOfPeople(e.target.value);
const list = document.getElementsByClassName('selectedNumberOfPeopleButton');
for (let i = 0; i < list.length; i++) {
list[i].classList.remove('hover');
}
this.toggleSelectedButtonState(e);
}
Does anyone have any guidance in what I'm doing wrong ?
It will be super !!
Many thanks !

Use this.props.selectNumberOfPeople not selectedNumberOfPeople.
<RaisedButton
className="selectedNumberOfPeopleButton"
onClick={this.props.selectNumberOfPeople}
value="1"
label="1"
labelPosition="before"
primary={true}
/>

you can also try
onClick={()=>this.props.selectedNumberOfPeople}

Related

onChange event fires on wrong mapped file input element

I am stuck on this issue with onChange handler being fired on a wrong element after .map function. I have a component, which I use to display mapped values, which looks like this:
const Step: React.FC<StepProps> = ({ status, title, text, onClick, onChange }) => {
return (
<button disabled={status === CompletionStatus.Completed} className={Styles.item} onClick={onClick}>
<mui.IconButton css={css.completionIcon} disabled={status === CompletionStatus.Completed}>
{status === CompletionStatus.Completed ? <muiIcons.Check /> : <Plus />}
</mui.IconButton>
<div className={Styles.content}>
<span className={status === CompletionStatus.Completed ? Styles.titleCompleted : Styles.title}>{title}</span>
<span className={status === CompletionStatus.Completed ? Styles.textCompleted : Styles.text}>{text}</span>
</div>
{onChange && (
<>
<label htmlFor="file-button" className={Styles.inputLabel} />
<input id="file-button" className={Styles.input} type={'file'} onChange={onChange} />
</>
)}
</button>
);
};
So, some of the mapped elements are being used with onClick, and two use onChange to gain photos from the user.
The issue is, that every time I trigger the onChange event on any of those inputs, only the first ones fires, e.g (I added this onChange function to test the name of the element that is being fired, and every time only the first one in the list is being console.logged)
onChange={(event: any)=> {
console.log(event, step);
event.target.value = null;
}}
So, I have figured out the issue here, maybe someone finds this helpful.
Having input with type file only having one id (file-button) was causing only the first such input to work
<label htmlFor="file-button" className={Styles.inputLabel} />
<input id="file-button" className={Styles.input} type={'file'} onChange={onChange} />
The way I fixed this, was basically having that id property unique, so I passed an index to the component and changed the id to
id={`file-button-${index}`}
Sounds like you may not have set a key for each item in your mapping function.
{yourData.map((item, index) => <Component key={`item-${index}`} item={item} />)}

React.js adding component to child

import FieldSect from "./fieldSect.js"
<div>
<FieldSect />
</div>
--FieldSect.js--
import Field from "./Field.js"
<div>
<Field />
<button onclick={addField}> addField </field>
</div>
--Field.js--
function Field (){
<div>
<label> Test </label>
<input type="text" />
</div>
}
My code works in the part where Field is loaded immediately and is shown correctly. I am really struggling on trying to figure out how to keep adding the <Field /> component under the already existing <Field /> whenever the add button is clicked. I also need to ensure to have the ability to keep adding rather than having a specific number of available
I also cannot use the DOM as I am getting an error telling me to edit the state rather than using DOM.render
End Result should look something like this:
--FieldSect.js--
<div>
<Field />
...<Field /> (Button Add Click)
...<Field /> (Button Add Click)
..
</div>
You should have Fields be part of the state of your parent component, perhaps as an array, this.state = {fields:[{id:1},{id:2},{id:3}]}.
Then, when rendering, you can iterate over the array:
<div>
{this.state.fields.map(f => <Field key={f.id}/>)}
</div>
Its super simple.
Have a default state of fields for example lets say that you want to have 1 field at the beginning
this.state = {
fields: [<Field/>]
}
Now use onClick event for AddClick and a function as follows
handleOnClick=(event)=>this.setState(prevState=>({...prevState, fields: [...prevState.fields, <Field/>]})
And in your render function iterate over fields array
PS: I am not sure what is a Field doing.
What I would do is in render
<div>
{this.state.fields.map(field => <Field {...field}/>)}
</div>
and fields would be the data of a field
If I'm understanding you correctly, you're wanting to add a every time someone clicks a button. If that's accurate, then I would simply add a constructor with a state to either the FieldSect.js or parent file to that (somewhere along the line before Field.js) that creates a trackable state. Something like:
this.state = {
NumberOfFields: 1
};
If you do this ahead of FieldSect.js, then it needs to be passed down in props.
Now you need to set up the onClick function to increment this state. Using this, you can then create a loop in FieldSect.js that creates an array of elements that will be rendered in React.
let array = []
for(let i = 0; i < this.NumberOfFields; i++){
array.push(<Field key={Math.random()}/>)
}
Then, instead of using in FieldSect.js, use {array}.
The reason I added the key attribute to the Field is that all elements that are derived from an iteration like this must have a unique key.
Here you go.
This holds the number of <Fields /> in the state of a controlled component, which can be incremented with the button.
The loop is generated by creating an Array of nbrOfFields length, destructured, and then mapped over.
const Field = () => (
<div>
<label> Test </label>
<input type="text" />
</div>
);
class App extends React.Component {
constructor() {
super();
this.state = {nbrOfFields: 1};
this.addField = this.addField.bind(this);
}
addField() {
this.setState(prevState => ({nbrOfFields: prevState.nbrOfFields + 1}));
}
render() {
return (
<div>
{[...Array(this.state.nbrOfFields)].map((item, i) => (
<Field key={i} />
))}
<button onClick={this.addField}>Add field</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Material-UI , Radio Button + map function ( React JS)

so i need help guys, i want to replace the old radio button (classic)
with a new one using material-ui. i couldn't make it.
please suggest a solution.
thx in advance.
in pic u will see everything
class Question extends Component {
render() {
var that = this; // Funny way to be able to access this from inside our iterator() function below.
var iterator = this.props.questionChoices.map(function(choice){
return (
<div className="radio">
<label>
<input type= {that.props.questionType} name={that.props.questionID} value={choice}/>
{choice}
</label>
<RadioButtonGroup >
<RadioButton
value="ludicrous"
label={choice}
valueSelected={choice}
style={styles.radioButton}
/>
</RadioButtonGroup>
</div>
);
});
return (
<div className="row">
<div className="form">
<Paper zDepth={1} >
<div className="h3">
<h3 >{this.props.questionID} - {this.props.questionText}</h3>
{iterator}
</div>
</Paper>
</div>
</div>
);
}
}
result of the problem image
You are rendering the old radio button as well, you also need to define the selected value in the group component and render the group only once, currently you are rendering it for every option.
var iterator = (
<RadioGroup value={this.state.value} onChange={this.handleChange}>
{ this.props.questionChoices.map(choice =>
<FormControlLabel value={choise} control={<Radio />} label={choise} />
)
}
</RadioGroup>
);
Then you need to create the handleChange function to update the state.
class Question extends Component {
state = {
value: '',
};
handleChange = (event, value) => {
this.setState({ value });
};
...
You can find a working example here: https://material-ui-next.com/demos/selection-controls/

React adding value to button from callback

render: function(){
var id = 2;
return(
<div>
<input type="text" ref="id" value={id} />
</div>
)
}
I know that I can add a value to a button using a variable (as above), however I am getting my initial value from a mysql database, which requires a callback function. I was using socket.io to try and get the value:
render: function(){
socket.emit('get_value')
socket.on('returned_value', function (value) {
//does not work
id = value
}
return(
<div>
<input type="text" ref="id" value={id} />
</div>
)
}
The emit works, but socket.on does not. I have also tried to wrap the render in the socket.io method to force it to wait, but it obviously does not work. I have no idea how to get the value from the app.js class to index.js dynamically.
In app.js:
socket.on('get_value', function () {
mysql.getCurrentValue('*', function (result) {
socket.emit('returned_value', result)
});
});
Trying to include the mysql script directly in index.js (ie mysql = require('mysql.js')) also causes the page not to render, but I dont know why. I works from app.js which is why im using socket.io.
I've put together a quick example. Put the socket code in the componentDidMount() method and set the id as state (this.setState({ id: id });. Then set the value of the input to this.state.id.
componentDidMount() {
socket.emit('get_value')
socket.on('returned_value', function (value) {
this.setState({id: id});
}.bind(this); // think you have to bind, didn't test
}
render() {
return(
<input type="text" ref="id" value={this.state.id} />
);
}

How to get Material-UI's <TextField/> to return correctly with ref='' like <input/> in ReactJS?

With the following method:
handleClick(event) {
const inputText = this.refs.inputText
console.log(inputText.value.trim())
}
I am trying to get Material-UI's <TextField/> to return the input text correctly with ref like the <input/> can with <button/> triggering it:
<input
className='form-control'
placeholder='Input Text'
ref='inputText'
type='text'
/>
<button
onClick={(event) => this.handleClick(event)}
>
And I attempted the following with <TextField/>, but it returns as undefined. How can I get it to return inputted text correctly like the <input/> above?
<TextField
hint='Enter text'
className='form-control'
ref='inputText'
type='text'
/>
I would suggest this approach:
Set up your textfield with a value and onChange function that are hooked into redux itself, where the onChange function just updates the value.
So you'd have something like this :
<TextField
value={this.props.textFieldValue}
onChange={this.props.textFieldChange}
Where the textFieldChange is an action that simply updates the textFieldValue. Most forms in redux will work something like this. Keep in mind the names i made up for those props and action are just for example. If you have a big form you might want to consider have part of the state tree dedicated to the form itself where you have :
state: {
form: {
textField: ...your textfield value here,
name: ...,
whateverElse: ...
}
};
I like doing this with redux because I can make that architect form part of the state to look like the json payload of wherever I'm sending it to, so there I can just send the form went I want to send it.
Anyways, back to this example. When you click your handleClick now. All you need to do is this to get the value:
handleClick(event) {
console.log(this.props.textFieldValue.trim());
}
Because the textfield is updated with every change, you always have access to it in your state. This also gives you flexibility over the refs approach, because if you use refs you will have a lot harder of a time getting access to that form in other components. With this approach, all the information is on your state so you can access it anytime, as long as you manage your props.
You should use the onChange={} to get the value:
_onChange = (e) => {
console.log(e.target.value);
}
<TextField
onChange={this._onChange}
/>
Here's a better solution than using onchange event, we get directly the value of the input created by material-ui textField :
create(e) {
e.preventDefault();
let name = this.refs.inputText.input.value;
alert(name);
}
constructor(){
super();
this.create = this.create.bind(this);
}
render() {
return (
<form>
<TextField ref="inputText" hintText="" floatingLabelText="Your name" /><br/>
<RaisedButton label="Create" onClick={this.create} primary={true} />
</form>
)}
hope this helps.

Categories