I'm learning react and made some components with inputs. I have several events where i use event.target.value, But the problem is that they overwrite each other.
How can I set a specific name for each event.target.value, something like event.target.myname.value
To understand in more detail please see here Code in stackblitz
Below is the code that I need to change and make events here with some kind of identifier so that they are not overwritten with other values.
class Range extends React.Component {
render() {
return (
<div>
<label for="f-size">Font size(from 1 to 50): {event.target.value}px</label>
<input
type="range"
id="f-size"
defaultValue="14"
name="fsize"
min="1"
max="50"
onChange={(event)=>this.props.fzCallback(event.target.value)}
/>
<p style={{ fontSize: `${event.target.value}px`}}>
Test text for font size range input.
</p>
</div>
);
}
}
export default Range;
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
fz: '',
};
}
handleFz = (fzData) => {
this.setState((prev) => ({ ...prev, fz: fzData }));
};
render() {
return (
<div>
<div>
<Range fzCallback={this.handleFz} />
</div>
</div>
);
}
}
export default App;
I would be very grateful if someone could help me solve this.
The problem is that you are using the Window event property which returns the current Event the handled by the site. (More on that here: https://developer.mozilla.org/en-US/docs/Web/API/Window/event)
The way to fix your problem is passing props to your child components.
So for example the Range component would look like that:
class Range extends React.Component {
render() {
return (
<div>
<label for="f-size">Font size(from 1 to 50): {this.props.value}px</label>
<input
type="range"
id="f-size"
defaultValue="14"
name="fsize"
min="1"
max="50"
onChange={(event)=>this.props.fzCallback(event.target.value)}
/>
<p style={{ fontSize: `${this.props.value}px`}}>
Test text for font size range input.
</p>
</div>
);
}
}
And your App component:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
inp: '',
bg: '',
fc: '',
fz: '',
};
}
handleColor = (colorData) => {
this.setState((prev) => ({ ...prev, fc: colorData }));
};
handleBg = (bgData) => {
this.setState((prev) => ({ ...prev, bg: bgData }));
};
handleFz = (fzData) => {
this.setState((prev) => ({ ...prev, fz: fzData }));
};
handleCallback = (childData) => {
this.setState({ inp: childData });
};
handleReset = () => {
this.setState({ inp: '' });
};
render() {
const divStyle = {
color: this.state.fc,
backgroundColor: this.state.bg,
};
return (
<div>
<div style={divStyle}>
<p>Start editing to see some magic happen :)</p>
<Input
parentCallback={this.handleCallback}
parentReset={this.handleReset}
/>
<Result title={this.state.inp} />
<Select colorCallback={this.handleColor} bgCallback={this.handleBg} />
<Range value={this.state.fz} fzCallback={this.handleFz} />
</div>
</div>
);
}
}
Related
I'm making a comment system with React Quill as my editor and Firebase Firestore. Each comment post gets stored in firestore. Each stored comment has a reply button, and when clicked, the editor should be populated with the comment content I want to reply to. Basically I need to populate my editor with the content stored in firestore database. Here's a screenshot as to watch I want to achieve:
Comment reply
Here's some code from the comment editor component
class NewComment extends Component {
constructor(props) {
super(props);
this.state = {
comment: {
commentID: "",
content: "",
createDate: new Date(),
featureImage: "",
isPublish: "True",
createUserID: "",
},
};
}
...
onChangeCommentContent = (value) => {
this.setState({
comment: {
...this.state.comment,
content: value,
},
});
};
...
render() {
return (
<Container>
<Row>
<Col xl={9} lg={8} md={8} sn={12}>
<h2 className={classes.SectionTitle}>Comment</h2>
<FormGroup>
<ReactQuill
ref={(el) => (this.quill = el)}
value={this.state.comment.content}
onChange={(e) => this.onChangeCommentContent(e)}
theme="snow"
modules={this.modules}
formats={this.formats}
placeholder={"Enter your comment"}
/>
</FormGroup>
</Col>...
The reply button is in a different component where I render the stored comments. Tell me if you need the full code from the components.
Here is a simple example on how to pass on information between two components via the parent component using function components:
// Index.js
const MyComponent = () => {
const [replyValue, setReplyValue] = useState("");
const onClick = (value) => {
setReplyValue(value);
};
return (
<>
<Comment value="This is a reply" onClick={onClick} />
<Comment value="This is another reply" onClick={onClick} />
<CreateReply quoteValue={replyValue} />
</>
);
};
// Comment.js
export const Comment = ({ value, onClick }) => {
return (
<div className="comment" onClick={() => onClick(value)}>
{value}
</div>
);
};
// CreateReply.js
export const CreateReply = ({ quoteValue = "" }) => {
const [value, setValue] = useState("");
useEffect(() => {
setValue(quoteValue);
}, [quoteValue]);
const onValueUpdated = (newValue) => {
if (newValue !== value) {
setValue(newValue);
}
};
return (
<>
<ReactQuill value={value} onChange={onValueUpdated} />
</>
);
};
Here is the same example using class components:
// Index.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = {
replyValue: ""
};
}
onClick = (value) => {
this.setState({
replyValue: value
});
};
render() {
return (
<>
<Comment value="This is a reply" onClick={this.onClick} />
<Comment value="This is another reply" onClick={this.onClick} />
<CreateReply quoteValue={this.state.replyValue} />
</>
);
}
}
// Comment.js
export class Comment extends React.Component {
render() {
return (
<div
className="comment"
onClick={() => this.props.onClick(this.props.value)}
>
{this.props.value}
</div>
);
}
}
// CreateReply.js
export class CreateReply extends React.Component {
constructor(props) {
super(props);
this.onValueUpdated = this.onValueUpdated.bind(this);
this.state = {
value: props.quoteValue
};
}
componentDidUpdate(prevProps) {
if (this.props.quoteValue !== prevProps.quoteValue) {
this.setState({
value: this.props.quoteValue
});
}
}
onValueUpdated = (newValue) => {
if (newValue !== this.state.value) {
this.setState({
value: newValue
});
}
};
render() {
return (
<>
<ReactQuill value={this.state.value} onChange={this.onValueUpdated} />
</>
);
}
}
class Demo extends React.Component{
constructor (){
super();
this.state = {
list : ['car','map', 'house']
}
}
inputValue(e){
var x = e.target.value;
console.log(x)
}
addValue(){
this.state.list.push();
this.setState({list: this.state.list});
}
render(){
return(
<div>
<input onChange={this.inputValue} type="text"/>
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
)
}
}
ReactDOM.render(
<Demo/>,
document.getElementById('test')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="test"></div>
Using my code, how can i push the value from <input onChange={this.inputValue} type="text"/> in list : ['car','map', 'house']. I use for this addValue function, but i can't insert the x variable from inputValue function in push() from addValue function. How to do this using my code?
You need a state value for the text-input so that your addValue() function knows what to use when its time to add a new item. The text state will be updated with anything the user types.
Working demo: https://codesandbox.io/s/magical-feynman-fze1n
import React from "react";
class Demo extends React.Component {
constructor() {
super();
this.state = {
text: "",
list: ["car", "map", "house"]
};
}
inputValue(e) {
this.setState({
text: e.target.value
});
}
addValue() {
const text = this.state.text;
this.setState({ list: [...this.state.list, text] });
}
render() {
return (
<div>
<input onChange={this.inputValue.bind(this)} type="text" />
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
);
}
}
export default Demo;
Also, refrain from doing direct state-mutations like this.state.list.push(blah). This is against React principles and can lead to unwanted visual side-effects. If you need to reference an existing state, try to create a copy of it instead. In the case for you list, we use the spread-operator to create a shallow-copy and then added the new item to the array..
Since React is all about small components and reusability consider breaking it up into two separate components... That way, if you need a form anywhere else you can reuse it...
Here is your Demo:
class Demo extends Component {
state = { list: ['car', 'map', 'house'] };
addItem = item => {
this.setState({ list: [item, ...this.state.list] });
};
render() {
return (
<div>
<Form addItem={this.addItem} />
{this.state.list.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
}
And here is the Form:
class Form extends Component {
state = { item: '' };
handleChange = event => {
this.setState({ item: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
this.props.addItem(this.state.item);
this.setState({ item: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.item}
onChange={this.handleChange}
/>
</form>
);
}
}
Live Demo: https://stackblitz.com/edit/react-611uzp
I'm building a simple chat app, but a new comment posted from the input field in the child component is not displayed.
--Parent component--
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
comments: [],
currentUser: { displayName: "user3", uid: 3 }
};
}
addComment = comment => {
this.setState(prevState => {
comments: prevState.comments.push(comment);
});
console.log("this.state");
console.log(this.state);
};
render() {
const { comments, currentUser } = this.state;
return (
<div className="App">
{comments.map(comment => (
<div className="line__left" key={comment.createdAt}>
<figure>
<i className="fas fa-user fa-4x" />
</figure>
<div className="line__left-text">
<div className="name">{comment.createdBy.displayName}</div>
<div className="text">{comment.text}</div>
</div>
</div>
))}
<ChatInputBox addComment={this.addComment} currentUser={currentUser} />
</div>
);
}
}
--Child component--
class ChatInputBox extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "",
currentUser: this.props.currentUser
};
}
handleChange = e => {
this.setState({ text: e.target.value });
};
handleClickPost = e => {
e.preventDefault();
let comment = {
createdAt: new Date().getTime(),
createdBy: this.state.currentUser,
text: this.state.text
};
this.props.addComment(comment);
this.setState({ text: "" });
};
render() {
const { text } = this.state;
return (
<div className="ChatInputBox">
ChatBox
<textarea onChange={this.handleChange} value={text} />
<button onClick={this.handleClickPost}>Post</button>
</div>
);
}
}
After I populate the text area and click the button, parent's state seems to be updated, but new comment is not shown.How can I show it?
change your code
addComment = comment => {
this.setState(prevState => {
comments: prevState.comments.push(comment);
});
console.log("this.state");
console.log(this.state);
};
to
addComment = comment => {
const { comments } = this.state;
this.setState({
comments: comments.concat(comment)
});
};
when you are using setState(), using concat instead of push since it maintains your array's immutability.
I have a question about why does not the "onClick" function work? It will only receive "You are not old enough!", when i hit the button. I use a input field.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state= {
term: 'write a number'
}
this.change = this.change.bind(this);
}
change = (event) => {
this.setState({term: event.target.value >= 18 ? <p>You are old enough!
</p> : <p>You are not old enough!</p>});
}
render() {
return (
<div style={{textAlign : "center"}}>
<input type="text"></input><br></br>
<p>Result</p><br></br>
{this.state.term}
<button type="submit" onClick={this.change}>Submit</button>
</div>
);
}
}
export default App;
If you want to validate the input on click, store the value of the input in state.
class App extends Component {
constructor() {
super();
this.state = {
term: 'write a number',
value: ''
};
}
handleChange = event => {
this.setState({
value: event.target.value
});
};
validate = () => {
this.setState({
term:
parseInt(this.state.value) >= 18
? 'You are old enough!'
: 'You are not old enough!'
});
};
render() {
return (
<div style={{ textAlign: 'center' }}>
<input
type="text"
onChange={this.handleChange}
value={this.state.value}
/>
<br />
<p>Result</p>
<br />
<p>{this.state.term}</p>
<button type="submit" onClick={this.validate}>
Submit
</button>
</div>
);
}
}
You can create a handler for the input and when you click in the button you get the value from the state.
Check it out my approach.
class App extends React.Component {
state = {
age: null,
term: 'write a number'
}
onClick = () => {
if(this.state.age) {
const output = this.state.age >= 18 ?
<p>You are old enough!</p> :
<p>You are not old enough!</p>
this.setState({
term: output
});
}
onInputHandler = (event) => {
this.setState({age: event.target.value})
}
render() {
return (
<div style={{textAlign : "center"}}>
<input type="text" onChange={e => this.onInputHandler(e)}></input><br></br>
<p>Result</p><br></br>
<button onClick={this.onClick}>Submit</button>
</div>);
}
}
I'm learning ReactJS and I'm creating a editable Pokémon list based on this guide.
When I try to pass a function to edit a list item (at this point I want to click the item and get the name), I get TypeError: Cannot read property 'edit' of undefined on the following line of the addPokemon function: onClick={() => this.edit(pokemon.name)}
Code:
PkmnForm
import React, { Component } from "react";
import PkmnList from "./PkmnList";
class PkmnForm extends Component {
static types = [
'Bug',
'Dragon',
'Ice',
'Fighting',
'Fire',
'Flying',
'Grass',
'Ghost',
'Ground',
'Electric',
'Normal',
'Poison',
'Psychic',
'Rock',
'Water'
]
constructor(props) {
super(props);
this.state = {
name: '',
type: '',
pokemons: [],
caught: false,
};
}
handleSubmit = (event) => {
var pokemon = {
name: this.state.name,
type: this.state.type,
caught: this.state.caught
};
this.setState({
name: '',
type: '',
caught: false,
pokemons: this.state.pokemons.concat(pokemon)
});
event.preventDefault()
}
handleChange = (event) => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleTypeChange = (event) => {
this.setState({
type: event.target.value,
})
}
editPokemon(name) {
console.log(name);
}
render() {
return (
<div>
<div>
<h1>Register a Pokémon</h1>
<form onSubmit={this.handleSubmit}>
<input
required
placeholder=" Name"
name="name"
onChange={this.handleChange}
value={this.state.name}
/>
<br />
<select
name="type"
required
value={this.state.type}
onChange={this.handleChange}
>
<option value='' disabled>Type</option>
{PkmnForm.types.map(
optionValue => (
<option
key={optionValue}
value={optionValue}
>
{optionValue}
</option>
)
)}
</select>
<br />
<label>
Caught
<input
name="caught"
type="checkbox"
checked={this.state.caught}
onChange={this.handleChange}
/>
</label>
<br />
<button type="submit">Add Pokemon</button>
</form>
</div>
<div>
<PkmnList
pokemons={this.state.pokemons}
edit={this.editPokemon}
/>
</div>
</div>
);
}
}
export default PkmnForm;
PkmnList
import React, { Component } from 'react';
class PkmnList extends Component {
constructor(props) {
super(props);
this.edit = this.edit.bind(this);
}
edit(name) {
this.props.edit(name);
}
addPokemon(pokemon) {
return <li
onClick={() => this.edit(pokemon.name)}
key={pokemon.name}
>
{pokemon.name} – {pokemon.type} {pokemon.caught ? '(caught)' : ''}
</li>
}
render() {
var pokemons = this.props.pokemons;
var listItems = pokemons.map(this.addPokemon);
return (
<ul>
{listItems}
</ul>
);
}
}
export default PkmnList;
Thanks :-)
The problem is in this line:
var listItems = pokemons.map(this.addPokemon);
Here, you are passing this.addPokemon as the function to map. But that function is not bound to this, so inside of it, this is not available.
You either have to bind it explicitly by calling .bind(this), just like you did with the edit function:
var listItems = pokemons.map(this.addPokemon.bind(this));
Or you can pass an arrow function that calls the method:
var listItems = pokemons.map(x => this.addPokemon(x));
You need to bind editPokemon like so:
constructor(props) {
super(props);
this.editPokemon = this.editPokemon.bind(this);
}
Or you can also use arrow functions, which have proper scoping:
editPokemon = (pokemon) => {
...
}