so i have a basic todo react app and how can i check if the user enters an empty value
How can i also make sure that the empty todo dosent get added to the todolist
Provide us a sample code that you are working with, it will be easy for us to suggest things.
Assuming you using class component. In addTask method you can check if the user has entered the text or not.
export default class App extends React.Component {
state = {
list: [],
task: ""
};
addTask = () => {
const { task, list } = this.state;
if (!task) {
alert("Please enter a task");
return false;
}
this.setState({ list: [...list, task] });
};
render() {
const { task, list } = this.state;
return (
<>
<input
value={task}
onChange={e => this.setState({ task: e.target.value })}
/>
<button onClick={this.addTask}>Add task</button>
<ul>
{list.map(task => (
<li>{task}</li>
))}
</ul>
</>
);
}
}
https://codesandbox.io/s/intelligent-yonath-clken?file=/src/App.js:51-736
I think what you require is similar to below code,
just check if trimmed value is not equal to "", then the trimmed item is added in your list.
function addItem() {
if(inputText.trim()!==""){
setItems(prevItems => {
return [...prevItems, inputText.trim()];
});
setInputText("");
}
}
Also make sure to save the index.js so that server is restarted before trying to access the App on local port.
Related
I have an app, let's call it Stopwatch so you know how it is working.
First, it have second meter which you can pause by pressing stop (stop button appears when pressing start), then you are able to start it again from the paused place or reset and start over.
If i use submit method in the form for saving time value and title, it works fine, but it reset time counter also and make stupid "white flash" after submitting. So i cant use it.
Problem of the case: I made button outside of the form, and it works almost fine. I want clear input with that button also. I tried these inside of the button function, but not working:
document.getElementById("title-value").reset();
document.getElementById("title-value").value = '';
Here is my code (and picture of app below):
import React from 'react';
class StopwatchHistory extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) {
event.preventDefault();
}
componentDidMount() {
this.setHistoryState();
}
setHistoryState = () => {
if (localStorage.times) {
this.setState({ history: localStorage.times.split('|') });
} else {
this.setState({ history: [] });
}
};
saveToLocalStorage = () => {
let titletieto = `${this.state.value}`; // form input value
// printing title and time to appearing list
if (localStorage.times) {
localStorage.times =
`${titletieto} ${this.props.formatTime(
this.props.currentTimeMin
)}:${this.props.formatTime(
this.props.currentTimeSec
)}:${this.props.formatTime(this.props.currentTimeMs, 'ms')}|` +
localStorage.times;
} else {
localStorage.times = `${titletieto} ${this.props.formatTime(
this.props.currentTimeMin
)}:${this.props.formatTime(
this.props.currentTimeSec
)}:${this.props.formatTime(this.props.currentTimeMs, 'ms')}|`;
}
};
saveTime = () => {
if (typeof Storage !== 'undefined') {
this.saveToLocalStorage();
} else {
console.error('local storage not supported');
}
this.setHistoryState();
};
// Remove times from Local Storage
resetHistory = () => {
if (localStorage.times) {
localStorage.removeItem('times');
}
this.setHistoryState();
};
render() {
return (
<div className={'stopwatch__history'}>
<div className="container">
<form id="title-value" onSubmit={this.handleSubmit}>
<label>
<input type="text" value={this.state.value} onChange={this.handleChange} /> </label>
</form>
</div>
<button onClick={this.saveTime}>SAVE TIME</button>
<button onClick={this.resetHistory}>RESET HISTORY</button>
<h3>History</h3>
<ul>
{this.state.history.map((item, index) => <li key={index}>{item}</li>)}
</ul>
</div>
);
}
}
export default StopwatchHistory;
Your input is controlled input. You have value set to be value={this.state.value} and handle change function is:
handleChange(event) { this.setState({value: event.target.value}); }
so if you want to reset this input, your reset function would simply set that state.value to be an empty string.
reset = () => {
this.setState({value: ""});
};
If your input would be uncontrolled (so value and onChange would not be provided) you could change it by accessing it through Ref. (see React.createRef).
I am trying to send the contents of the text box to be displayed in the spam when I click on the button
class Hello extends Component {
state = {
texto: ""
}
changeText = () =>{
this.setState({texto: this.state.texto})
}
render() {
return (
<div>
<input type = "text" defaultValue = {this.state.texto}></input>
<p>{this.state.texto}</p>
<button onClick = {this.changeText}>Click</button>
</div>
);
}
}
export default Hello;
function App() {
return (
<div>
<Hello />
</div>
);
}
The idea is that when you click on the button it shows the ones in the text box inside the span.
I am new to react and this clarifies some doubts about the concepts.
thanks guys.
Carlos!
First of all, you must update your input so your code can work properly. In your input, do this instead:
<input type="text" value={this.state.texto} onChange={() => this.onChangeHandler(e.target.value)}></input>
Then, inside your class, you create an onChangeHandler to deal with the data from input first:
onChangeHandler = (e) => {
this.setState({
texto: e
});
}
Ok, we're almost there, now you must create another state item, so you can use it for your final input:
state = {
texto: "",
textoFinished: ""
}
Then, correct your onChange envent:
changeText = () =>{
this.setState({textoFinished: this.state.texto})
}
Now, to access the new value, just go to this.state.textoFinished. Happy coding!
You need to update the state using target value after clicking on it.
Something Like this :
state = {
texto: ""
}
onChangeHandler = (e) => {
const value = e.target.value;
this.setState({texto: value});
}
OR
onChangeHandler = (e) => {
const texto = e.target.value;
this.setState({texto});
}
I'm sure I'm doing something silly here. I'm trying to write a component which, based on the fetching state of the data, renders one of 3 things.
1) An error message if the fetch has errored.
2) A loading message if the data is fetching.
3) The full child component if the data is fetched.
What's happening now is the fetch succeeds, but the Loading message won't disappear. In the code example below, you can see that I've added a console.log for whether or not the full render should occur. What's perplexing me is that this will eventually log as true, but the Loading message still shows.
This exact pattern seems to work for a sibling component in this app, so I'm confused as to what's missing here... Here's the entire component, it's been anonymized and stripped of styles, but the structure is otherwise identical.
function mapStateToProps(state) {
return {
Data: state.NewDataReducer,
};
}
const mapDispatchToProps = {
fetchNewData: ActionCreators.fetchNewData,
};
const DataError = () => (
<div>
Error fetching new data!
</div>
);
const DataLoading = () => (
<div>
Loading..
</div>
);
class MainContainer extends PureComponent {
static propTypes = {
choice: PropTypes.string,
fetchNewData: PropTypes.func,
Data: ImmutablePropTypes.map.isRequired,
}
state = {
choice: null,
}
componentDidUpdate(nextProps) {
const { choice, fetchNewData, Data } = this.props;
if(!choice) {
return;
}
if(isFetching(Data)) {
return;
}
const newChoiceSelected = choice !== nextProps.choice;
if(newChoiceSelected) {
fetchNewData({choice});
}
}
handleChangeChoice = (choice) => {
this.setState({
choice: { choice }
});
}
render() {
const { choice, Data } = this.props;
const error = hasFetchError(Data);
const loading = !error && !isFetched(Data);
const renderFull = !loading && !error;
console.log(renderFull);
if(!renderFull) {
return (
<div>
Please wait.
</div>
);
}
const { dataBreakdown } = Data.get("dataKey").toJS();
return (
<div>
<ChildComponent
choice={choice}
dataBreakdown={dataBreakdown}
onSetDrillDown={this.handleChangeChoice}
/>
</div>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(MainContainer);
You need to pass your map function the arguments of what it needs to map over from immutable data types. Map creates a new a array and makes a callback function on the items that it iterates through in that array. This is just an example
Data: ImmutabaleProptypes.map((ing, index)=>(
<li key={index}>{Data}</li> // you can write jsx in here.
));
Newbie React question here on show hide functionality.
I have a state of 'show' that I set to false:
this.state = {
show: false,
};
Then I use the following function to toggle
toggleDiv = () => {
const { show } = this.state;
this.setState({ show : !show })
}
And my display is
{this.state.show && <xxxxxx> }
This all works fine. However I want to apply the function it to multiple cases (similar to accordion, without the closing of other children. So I change my constructor to
this.state = {
show: [false,false,false,false,false,false]
};
and this to recognise there are 6 different 'shows'.
{this.state.show[0] && <xxxxxx> }
{this.state.show[1] && <xxxxxx> } etc
But where I get stuck is how to account for them in my toggleDiv function. How do I insert the square bracket reference to the index of show (if this is my problem)?
toggleDiv = () => {
const { show } = this.state;
this.setState({ show : !show })
}
Thanks for looking.
First of all I'd suggest you not to rely on current state in setState function, but to use the callback option to be 100% sure that you are addressing to the newest state:
this.setState((prevState) => ({ show: !prevState.show }));
How to deal with multiple elements?
You'll have to pass the index of currently clicked element.
{yourElements.map((elem, i) => <YourElem onClick={this.toggleDiv(i)} />)}
and then inside your toggleDiv function:
toggleDiv = (i) => () => {
this.setState((prevState) => {
const r = [...prevState.show]; // create a copy to avoid state mutation
r[i] = !prevState.show[i];
return {
show: r,
}
}
}
Use an array instead of a single value. In your toggle div function make a copy of the state array make necessary changes and push the entire array back up to state at the end.
This is some simplified code showing the workflow I described above
export default class myClass extends React.Component{
constructor(props){
super(props);
this.state = { show: new Array(2).fill(false) };
}
//you need a index or id to use this method
toggleDiv = (index) => {
var clone = Object.assign( {}, this.state.show ); //ES6 Clones Object
switch(clone[index]){
case false:
clone[index] = true
break;
case true:
clone[index] = false
break;
}
this.setState({ show: clone });
}
render(){
return(
<div>
{ this.state.show[0] && <div> First Div </div> }
{ this.state.show[1] && <div> Second Div </div> }
{ this.state.show[2] && <div> Third Div </div> }
</div>
)
}
}
I'm trying to debounce a component in my webapp. Actually it is a filter for the maxPrice and if the user starts to print, the filter starts to work and all the results disappear until there is a reasonable number behind it.
What I tried so far:
import _ from 'lodash'
class MaxPrice extends Component {
onSet = ({ target: { value }}) => {
if (isNaN(Number(value))) return;
this.setState({ value }, () => {
this.props.updateMaxPrice(value.trim());
});
};
render() {
const updateMaxPrice = _.debounce(e => {
this.onSet(e);
}, 1000);
return (
<div>
<ControlLabel>Preis bis</ControlLabel><br />
<FormControl type="text" className={utilStyles.fullWidth} placeholder="egal"
onChange={updateMaxPrice} value={this.props.maxPrice}
/>
</div>
);
}
}
I'm getting the error
MaxPrice.js:11 Uncaught TypeError: Cannot read property 'value' of null
at MaxPrice._this.onSet (MaxPrice.js:11)
at MaxPrice.js:21
at invokeFunc (lodash.js:10350)
at trailingEdge (lodash.js:10397)
at timerExpired (lodash.js:10385)
In my old version I had onChange={this.onSet} and it worked.
Any idea what might be wrong?
As you mentioned in comments, it's required to use event.persist() to use event object in async way:
https://facebook.github.io/react/docs/events.html
If you want to access the event properties in an asynchronous way, you
should call event.persist() on the event, which will remove the
synthetic event from the pool and allow references to the event to be
retained by user code.
It means such code, for example:
onChange={e => {
e.persist();
updateMaxPrice(e);
}}
Here is my final solution. Thanks to lunochkin!
I had to introduce a second redux variable so that the user see's the values he is entering. The second variable is debounced so that the WepApp waits a bit to update.
class MaxPrice extends Component {
updateMaxPriceRedux = _.debounce((value) => { // this can also dispatch a redux action
this.props.updateMaxPrice(value);
}, 500);
onSet = ({ target: { value }}) => {
console.log(value);
if (isNaN(Number(value))) return;
this.props.updateInternalMaxPrice(value.trim());
this.updateMaxPriceRedux(value.trim());
};
render() {
return (
<div>
<ControlLabel>Preis bis</ControlLabel><br />
<FormControl type="text" className={utilStyles.fullWidth} placeholder="egal"
onChange={e => {
e.persist();
this.onSet(e);
}} value={this.props.internalMaxPrice}
/>
</div>
);
}
}
function mapStateToProps(state) {
return {
maxPrice: state.maxPrice,
internalMaxPrice: state.internalMaxPrice
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({updateMaxPrice:updateMaxPrice,
updateInternalMaxPrice:updateInternalMaxPrice}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(MaxPrice);