I'm new to javascript and I want to run some code depending if the state.value != null or "".it doesn't throw an error but freezes there. please see my code down below.any help would be really appreciated.
constructor(){
super();
this.state = {
value:null,
list:[]
}
}
handleList = () => {
//let list = this.state.list.slice();
if (this.state.value != null || this.state.value.length() > 0 ) {
let list = [...this.state.list];
list.push(<li>{this.state.value}</li>);
this.setState({list});
console.log(list.length,this.state.list.length);
}else{
console.log("Cant enter null");
}
}
render() {
return(
<div className = 'global'>
<button onClick={() => {this.handleList()}
}>Add-New</button>
<input
onChange = {
(e)=>{this.setState({value: e.target.value})}
}
type = 'text'
placeholder = 'Enter New Todo!'/>
<hr/>
<ul>
{
this.state.list.map((li) => {
return (li);
})
}
</ul>
</div>
);
}
}
Evaluating the existence of Strings
In JavaScript: empty Strings '' are falsey (evaluate to false).
const x = ''
if (x) console.log('x = true')
else console.log('x = false')
As a result, the existence of this.state.value be tersely verified as follows:
if (this.state.value) .. // Do something if this.state.value != ''
This strategy can be leveraged and chained by simply referencing variables followed by && (which results in only the last truthy variable being returned). If no truthy variable is found: false is returned. ie. in the case of the onClick method of the <button/> tag below.
Rendering Lists
In React: it is typical to store lists of plain variables (Strings, Objects, etc) and handle conversion to element form on the fly.
Rendering Strings representing HTML elements is a security flaw. In production: someone could very easily type a malicious todo and ruin your entire application. You may need to use dangerouslySetInnerHTML if you wish to continue down that path.
See the docs for more info on how to render lists.
Example
See below for a rough example of a todo container.
// Container.
class Container extends React.Component {
// Constructor.
constructor(props) {
super(props)
this.state = {
value: '',
list: []
}
}
// Render.
render = () => (
<div className = 'global'>
<button onClick={() => this.state.value && this.setState({value: null, list: [...this.state.list, this.state.value]})}>Add</button>
<input value={this.state.value} onChange={(e) => this.setState({value: event.target.value})} placeholder="Todo.."/>
<hr/>
<ul>
{(this.state.list.length > 0 && (this.state.list.map((todo, index) => <li key={index}>{todo}</li>))) || '..'}
</ul>
</div>
)
}
// Mount.
ReactDOM.render(<Container/>, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Because you are using OR, both criteria are checked. So even if value is NULL, the code is still attempting to check the length of the string. But a NULL object doesn't have a "length" property, so this will result in an "value does not have property: length" error. To fix this, using AND ( && ) would be more appropriate.
Additionally, the "length" property is a value, not a function, so attempting to call as function will result in an "length is function of value" error.
These errors should appear in the console when viewing your web-page. If you press F12, a window should appear at the bottom of your browser. If you then select the console tab, you should be able to see all errors output. You might need to make sure you aren't filtering error messages.
Related
My state.events is in array that is made up of the component instance: EventContainer.
I want my setState to place a new EventContainer in the state.events array. However, I want that EventContainer to go in the index immediately after the specific EventContainer that made the setState call.
I'm looking for help with making the adjustments necessary to my approach or, if my entire approach is bad, a recommendation on how to go about this. Thank you very much.
I'm developing an itinerary builder which is made up of rows/EventContainers that represent an activity on a given day.
Each EventContainer has a button that needs to offer the user the ability to onClick an additional row immediately after that EventContainer.
class DayContainer extends React.Component {
constructor(props){
super(props);
this.state = {
events: [],
};
this.pushNewEventContainerToState = this.pushNewEventContainerToState.bind(this);
}
pushNewEventContainerToState (index){
let newEvent = <EventContainer />;
this.setState(prevState => {
const events = prevState.events.map((item, j) => {
if (j === index) {
events: [...prevState.events.splice(index, 0, newEvent)]
}
})
})
}
render(){
return (
<>
<div>
<ul>
{
this.state.events === null
? <EventContainer pushNewEventContainerToState= .
{this.pushNewEventContainerToState} />
: <NewEventButton pushNewEventContainerToState={this.pushNewEventContainerToState} />
}
{this.state.events.map((item, index) => (
<li
key={item}
onClick={() =>
this.pushNewEventContainerToState(index)}
>{item}</li>
))}
</ul>
</div>
</>
)
}
}
My goal in setState was to splice newEvent into this.state.events immediately after the index (the parameter in pushNewEventContainerToState function).
I'm getting this error but I'm guessing there's more going on than just this: Line 23:22: Expected an assignment or function call and instead saw an expression no-unused-expressions.
I can see at least 2 issues with the code.
- Splice will mutate the array in place
- You are not returning the updated state.
You can instead use slice to build the new array.
pushNewEventContainerToState(index) {
let newEvent = < EventContainer / > ;
this.setState(prevState => {
const updatedEvents = [...prevState.events.slice(0, index], newEvent, ...prevState.events.slice(index + 1];
return {
events: updatedEvents
})
})
}
As I'm fairly new to coding, it took me awhile but I was able to compile the full answer. Here is the code, below. Below that, I explain, point by point, what the problem was and how the updated code addresses that.
class DayContainer extends React.Component {
constructor(props){
super(props);
this.state = {
events: [{key:0}],
};
this.pushNewEventContainerToState = this.pushNewEventContainerToState.bind(this);
}
pushNewEventContainerToState(index) {
let newEvent = {key: this.state.events.length};
this.setState(prevState => {
let updatedEvents = [...prevState.events.slice(0, index + 1), newEvent, ...prevState.events.slice(index + 1)];
return {
events: updatedEvents
};
})
}
render(){
return (
<>
<div>
<ul>
{this.state.events.map((item, index) => (
<li key={item.key}>
< EventContainer pushNewEventContainerToState={() => this.pushNewEventContainerToState(index) } / >
</li>
))}
</ul>
</div>
</>
)
}
}
Setup
Starting with state.events, instead of starting with an empty array, I'm starting with one object, including a key starting at 0, because I always want the user to start with one EventContainer.
Regarding pushNewEventContainerToState, #Sushanth made a great recommendation. Please refer directly to that function in my latest code. The refinement I made has to do with the way I separate the EventContainer being passed to this.state.events. I've moved the EventContainer from pushNewEventContainerToState down to the render() element. I've given it a prop of key={item.key} and wrapped the component instance in a li. The very first EventContainer will have a key of 0 (see state.events[0]). Now, each new EventContainer passed to state.events will have a key that's based off the latest .length() of the state.events array (refer to the latest value of the let newEvent variable in pushNewEventContainerToState).
All of that allowed me to fix a big problem I was facing: I needed the newest EventContainer to be placed in the index immediately after the index of the EventContainer calling pushNewEventContainerToState. The main reason this was happening was because I wasn't properly passing the index to the EventContainer inside of render(). Now that I have the actual EventContainer there, I can pass it a prop in the right manner (please refer EventContainer's prop in render). Now I'm calling pushNewEventContainerToState with the correct index.
I'm writing a React component that takes a total number of reviews as a prop. When the number of reviews is 0, I want to render an element stating
<div>No reviews yet.</div>
Otherwise, I render elements containing review data. Here is the component and its context:
const Stats = ({good, neutral, bad, totalReviews}) => {
if ({totalReviews} === 0)
return <div>No reviews yet.</div>
else {
return (
<div>
<Stat text="Total: " amount={totalReviews} />
</div>
);
}
}
const App = () => {
const [good, setGood] = useState(0);
const [neutral, setNeutral] = useState(0);
const [bad, setBad] = useState(0);
let totalReviews = good + neutral + bad;
return (
<div>
<Stats totalReviews={totalReviews} />
</div>
)
}
I have used the debugger command to check in Chrome's developer console the values of each variable. It shows that totalReviews = 0. The variables good, neutral, and bad all also = 0.
I've also used console.log(totalReviews).
0 is displayed by the console.log. How come my program enters the second conditional as if totalReviews isn't 0?
if (totalReviews === 0)
You only wrap js statements in curly braces inside jsx, but your if statement is just regular js.
Problem with your if condition.
it should be if (totalReviews === 0) or if (totalReviews == 0) to avoid strongly type conversation check.
You have added {} inside if condtion which is not a stadard way
I'm trying to change the state of opening/closing times (and the post to an endpoint) on multiple days which are return to me in an object like so:
I have rendered these in a React component:
{this.state.time.map(each => (
<Fragment key={each.code}>
<OpeningHours
code={each.code}
day={each.description}
name={each.code}
open={each.open !== null ? each.open : '00:00'}
close={each.close !== null ? each.close : '00:00'}
onChange={this.onTimeChange}
/>
</Fragment>
))}
How would I iterate through the array of objects until I find the index of either the opening or closing time of this day? So far I've tried this, which works, but only if there was, say, just an opening time or one field in general. The issue arises since I have 2 fields to edit:
onTimeChange(e) {
let inputs = this.state.inputs.slice();
for(let i in inputs){
if(inputs[i].name == event.target.name){
inputs[i].value = event.target.value;
this.setState ({inputs});
break;
}
}
}
const newInputs = inputs.map(p =>
p.name === <unique_name/code>
? { ...p, [e.target.name]: e.target.value }
: p
);
this.setState({inputs:newInputs,});
Change the onTimeChange() method to get the unique_name:onTimeChange(e, code)
A simple example: https://codesandbox.io/s/v1y14rl2oy
Since index is unique, you can do
inputs[index] = { ...inputs[index], [e.target.name]: e.target.value }
too without using map() as in the sandbox.
I´m trying to make a React calculator. It´s mostly done, but I have one problem I don´t know how to correct: I can´t seem to limit the number of operators an user can enter (for instance, I want to limit "++++" to just "+" and also prevent two operators getting joined: +- must become -). I tried resetting the state everytime an user enters an operator, but no dice. I´m seriously lost here. I thought about a Regex, but it seems to be rather problematic (way too many contexts to try).
class Calculator extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.handleClick = this.handleClick.bind(this);
}
handleClick(evt) {
const id = evt.target.id;
const result = evt.target.value;
this.setState(prevState => ({
value: `${prevState.value}${result}`.replace(/^0+\B/, '')
}));
if (id === 'equals') {
this.setState({ value: math.eval(this.state.value) });
} else if (id === 'clear') {
this.setState({ value: 0 });
}
}
}
You could use regular expressions to solve this. The main problem with using the includes() based approach is that it does not enforce a correct format in the input string. Perhaps you could use a regular expression like this?
/^\d*([/\+-/*=]\d+)*$/gi
This would prevent problems like multiple operands like +++, and so on:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = { value: "" };
this.handleClick = this.handleClick.bind(this);
}
handleClick(evt) {
const result = evt.target.value;
// Update state
this.setState({
value: result
});
// Comine previous state with input value
//const combination = `${ this.state.value }${result}`;
console.log(result, 'combination', result)
// Use regular expression to check valid input. If invalid
// prevent further processing
if(!result.match(/^\d*([/\+-/*=]\d+)*$/gi)) {
console.error('Invalid input')
return
}
this.setState({ calculated: eval(result) });
}
render() {
return <h1>
<input value={this.state.value} onChange={(e) => this.handleClick(e)}/>
<p>{ this.state.calculated }</p>
</h1>
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
There is a functioning JSFiddle here for you to try out
You could save a list with operations and the crrently typed character. Then before inserting check if it's an operation to prevent adding multiple of them
example
const ops = ['/', '+', '-', '^']
this.setState({lastChar: result})
// Before setState with the full formula
if(this.state.lastChar === result && ops.includes(result)) else if (ops.includes(this.state.lastChat) && ops.includes(result)) return;
assuming result is only the typed/clicked character
problems
This strategy is definitely not perfect. For example, typing 1+-2 or 1*-3 wouldn't work. But from here on you could tweak it to fit your needs
I'm on my phone, sorry for the bad formatting
I am building a test app to learn more about React and I have made an API call which gets a huge JSON object.
I was able to break this json into the parts that I need and now I have 10 arrays of 3 props each. I am able to send these 10 arrays in 3 props to another component, which needs to use these 3 props 10 times and render a div class Card each.
I can console.log(this.props) and it shows 10 different arrays with 3 props each,however, I cannot produce a same element 10 times.. I tried using map() but since my array is initially undefined, map() is not able to function properly either. Is there any thing in react like *ngFor in Angular ?
What is the best way to go about this?
*EDIT
Here's more code guys. Sorry still noobie here..
ERROR : this.props.map is not a function
return(
<div>
{this.props.map((data,i)=>{
return(
<li key={i}>{data.likes}</li>
);
*EDIT 2
Soo I tried running map function with an if condition but the code still breaks the very moment the condition gets true..
render() {
if(this.props.url !== undefined){
this.props.map((data,i) =>{
return <li key={i}>{data.likes}</li>
})
}
My state method is :
state = {
userId: undefined,
likes: undefined,
url: undefined
}
and im setting my values on each data stream as follows :
const pics = await fetch(`${base_url}?key=${api_key}&q=${query}
&img_type=photo&per_page=12`).then(response => {
return response.json();
})
pics.hits.map((data) =>{
return this.setState({
userId: data.user_id,
likes: data.likes,
url: data.webformatURL
})
})
this.props won't have map, it's not an array. It's an object with a property for each property passed to your component. For instance:
<YourComponent foo="bar"/>
...will have this.props.foo with the value "bar".
So if you're passing an array to your component, like this:
<YourComponent theArrayProperty={[{likes: 42},{likes:27}]} />
...then you need the name of that property:
return (
<div>
{this.props.theArrayProperty.map((data,i) => {
return (
<li key={i}>{data.likes}</li>
);
})}
</div>
);
Side note: You can use a concise arrow function for the map callback instead:
return (
<div>
{this.props.theArrayProperty.map((data,i) => <li key={i}>{data.likes}</li>)}
</div>
);
...and no need for the () if you put the opening tag on the line with return (you can't leave off the ( if it's on the next line, but you probably knew that):
return <div>
{this.props.theArrayProperty.map((data,i) => <li key={i}>{data.likes}</li>)}
</div>;
...but that's a matter of style.
With little information that you have provided, my guess is that code fails at map() when you try to use it with undefined value.
Try adding a conditional check to render
{props && props.map([RENDER CODE HERE])}
You can just make simple if statement to check if the array is not undefined, and then pass it to map function.
Another option is to set a defaultProps for an empty array.
MyComponent.defaultProps = {
arrProp: []
};