export default function App() {
const [activeKey, setActiveKey] = useState(0);
const [storeArr, setStoreArr] = useState([]);
function click(selector) {
setActiveKey(selector);
if (activeKey !== "=") {
setStoreArr([...storeArr, selector]);
setActiveKey(selector);
}
}
function clear() {
setStoreArr([]);
setActiveKey(0);
}
function calculate(storeArr) {
// combine consecutive numbers into a single number
for (let i = 0; i < storeArr.length; i++) {
if (
typeof storeArr[i] === "number" &&
typeof storeArr[i + 1] === "number"
) {
storeArr[i] = storeArr[i] * 10 + storeArr[i + 1];
storeArr.splice(i + 1, 1);
i--;
}
}
// perform multiplication and division first
for (let i = 0; i < storeArr.length; i++) {
if (storeArr[i] === "x") {
storeArr[i - 1] = storeArr[i - 1] * storeArr[i + 1];
storeArr.splice(i, 2);
i--;
} else if (storeArr[i] === "/") {
storeArr[i - 1] = storeArr[i - 1] / storeArr[i + 1];
storeArr.splice(i, 2);
i--;
}
}
// perform addition and subtraction
for (let i = 0; i < storeArr.length; i++) {
if (storeArr[i] === "+") {
storeArr[i - 1] = storeArr[i - 1] + storeArr[i + 1];
storeArr.splice(i, 2);
i--;
} else if (storeArr[i] === "-") {
storeArr[i - 1] = storeArr[i - 1] - storeArr[i + 1];
storeArr.splice(i, 2);
i--;
}
}
console.log(calculate(storeArr));
// return the result
return storeArr[0];
}
const topFuncs = [
{
name: "clear",
text: "AC"
},
{
name: "divide",
text: "/"
}
];
const numsDiv = [
{
name: "seven",
text: 7
},
{
name: "eight",
text: 8
},
{
name: "nine",
text: 9
},
{
name: "four",
text: 4
},
{
name: "five",
text: 5
},
{
name: "six",
text: 6
},
{
name: "one",
text: 1
},
{
name: "two",
text: 2
},
{
name: "three",
text: 3
},
{
name: "zero",
text: 0
},
{
name: "decimal",
text: "."
}
];
const rightDiv = [
{
name: "multiply",
text: "x"
},
{
name: "subtract",
text: "-"
},
{
name: "add",
text: "+"
},
{
name: "equals",
text: "="
}
];
return (
<div className="App">
<div id="outer-div" className="outer-div">
<div className="store-display">{storeArr}</div>
<div id="display" className="display">
{activeKey}
</div>
<div className="left-right">
<div id="left-div" className="left-div">
<div className="top-funcs">
{topFuncs.map((i) => (
<button
id={i.name}
className="btn"
key={i.text}
onClick={() => {
if (i.text === "AC") {
return clear();
} else {
click(i.text);
}
}}
>
{i.text}
</button>
))}
</div>
<div id="nums-div">
{numsDiv.map((i) => (
<button
id={i.name}
className="btn"
key={i.text}
onClick={() => {
click(i.text);
}}
>
{i.text}
</button>
))}
</div>
</div>
<div id="right-div" className="right-div">
{rightDiv.map((i) => (
<button
id={i.name}
className="btn"
key={i.text}
onClick={() => {
if (i.text === "=") {
calculate();
} else {
click(i.text);
}
}}
>
{i.text}
</button>
))}
</div>
</div>
</div>
</div>
);
}
i tried console logging but nothing shows on the console one the button is clicked
TypeError
Cannot read properties of undefined (reading 'length')
but that literally makes no sense to me, it doesnt understand what .length means?
i tried console logging but nothing shows on the console one the button is clicked
TypeError
Cannot read properties of undefined (reading 'length')
but that literally makes no sense to me, it doesnt understand what .length means?
i tried console logging but nothing shows on the console one the button is clicked
TypeError
Cannot read properties of undefined (reading 'length')
but that literally makes no sense to me, it doesnt understand what .length means?
i tried console logging but nothing shows on the console one the button is clicked
TypeError
Cannot read properties of undefined (reading 'length')
but that literally makes no sense to me, it doesnt understand what .length means?
i tried console logging but nothing shows on the console one the button is clicked
TypeError
Cannot read properties of undefined (reading 'length')
but that literally makes no sense to me, it doesnt understand what .length means?
i tried console logging but nothing shows on the console one the button is clicked
TypeError
Cannot read properties of undefined (reading 'length')
but that literally makes no sense to me, it doesnt understand what .length means?
Shadowing a variable is an error waiting to happen. Which is exactly what you're doing here:
function calculate(storeArr) {
Within the scope of that function, storeArr doesn't refer to the state value. It refers to the argument passed to the function. And what argument do you pass to the function? Nothing at all:
onClick={() => {
if (i.text === "=") {
calculate(); // <--- here
} else {
click(i.text);
}
}}
So within the function, storeArr is undefined. And, exactly as the error is telling you, you can't read a property from undefined.
Edit: Don't Mutate State Either
I just noticed that within your calculate function you are modifying the array. Never mutate state in React. So in this case it sounds like you need to:
Actually pass a value to your function.
Pass that value as a new array, not the existing state value.
Rename the local variable in the function, since it's confusing you.
So call the function with the new array:
onClick={() => {
if (i.text === "=") {
calculate([...storeArr]); // <--- here
} else {
click(i.text);
}
}}
(Additionally, at least for future readers, also make sure that if this is an array of objects that you don't mutate any of the objects in the new array, since they're the same object references back to state. Deep cloning arrays/objects is another matter altogether.)
And give the local variable a different name:
function calculate(sArr) {
And, of course, update all references within the function to the new local variable and not the state variable.
As an added improvement, since the function relies only on what is passed to it and not on component state, move the function outside of the component. It can be its own module, or just outside the component within the same module. This will further avoid confusion as it makes it more clear that the function is usable in isolation and not dependent on component state.
Original Answer:
Don't shadow the variable:
function calculate() {
That way within the function you're referring to the state value, not a local variable. And you can then remove the unnecessary argument from other uses of the function. So this:
console.log(calculate(storeArr));
Can become this:
console.log(calculate());
After all, why would you need to pass a value to a function which already exists within the same scope and can already access that value?
If you do want to pass a value to the function, give the function's local variable(s) a different name than one you're already using.
You're getting the error because your calculate function expects one argument, but you're not passing it anything in your onClick handler:
onClick={() => {
if (i.text === "=") {
calculate();
} else {
click(i.text);
}
}}
This causes the storeArr argument in the calculate function to equal undefined, so then when you try iterating over it in the for loop, it throws an error because you can't access the .length property on an undefined value.
Related
im trying to make an array with objects but while looping i get as a result the first 83 objects as undefiend and only the last 4 with correct data. I tried to refactor the code several times but i dont seem to find a solution.
This is the console log result i get
This is the network response i get from the API
<script>
export default {
computed: {
allSales(){
var i, sales=[], x, y
for (i = 0; i <= this.salesLists.length; i++) {
sales[i] = {
x:this.date(i+1),
y:this.amount(i+1),
status:this.status(i+1),
}
}
console.log(sales);// first 83 objects undefined
return sales
},
salesLists() {
this.$store.state.sale.sales
},
},
methods:{
date(id) {
return this.salesLists.filter(sale => sale.id === id).map(sale => new Date(sale.updated_at).toISOString().slice(0,10))[0];
},
amount(id) {
return this.salesLists.filter(sale => sale.id === id).map(sale => sale.amount)[0];
},
status(id) {
return this.salesLists.filter(sale => sale.id === id).map(sale => sale.status)[0];
}
}
}
After looking at your second screenshot, I see that your salesLists has elements with ids greater than 87, or the length of the salesLists array. This is an issue, because in your for loop you are assuming that every element of the array has an id that is >= 1 and <= salesLists.length.
Because this is not the case, there are several iterations of the loop where your date, amount, and status methods return undefined.
I would recommend that you transform the salesLists array directly in the computed method in a single call to map. It might look something like this:
allSales(){
return salesLists.map(sale => {
return {
x: new Date(sale.updated_at).toISOString().slice(0,10),
y: sale.amount,
status: sale.status
}
})
},
I have an Array that has objects inside that looks like this
OrchardSite > Array(16) > 0: {blocks: Array (3), name: "asdasd", code: "R101A"}
1: {blocks: Array (7), name: "dasdas", code: "R555"}
I have JSX that looks like this
<select className="input-field-slider dropdown w-select" onChange={() => this.setOrchard}>
<option>All Sites</option>
{orchardSites
.map((orchardSite, i) => (
<option key={i} value={orchardSite.code}>
{orchardSite.code + " " + orchardSite.name}
</option>
))}
Every time the user selects an option from the dropdown I have this function that fires..
setOrchard = (event) => {
const orchardId = event.target.value;
console.log("orchardID", orchardId) // returns selected ID
console.log("OrchardSite", this.props.metadata.OrchardSites) // returns array
console.log("testing1", this.props.metadata.OrchardSites[parseInt("R1018A")] // undefined
console.log("testing2", this.props.metadata.OrchardSites[parseInt(orchardId)]) // undefined
console.log("new value", this.props.metadata.OrchardSites[parseInt(orchardId)].blocks) // Cannot read property 'blocks' of undefined
if (orchardId.length > 0) {
if (!this.props.metadata.OrchardSites[parseInt(orchardId)].blocks)
this.props.metadata.orchardSites[parseInt(orchardId)].blocks = []
console.log("SetttingOrchardLocation", this.props.metadata.orchardSites)
this.setState({ orchardId: orchardId, block: null, area: null, row: null, site: Object.assign({}, this.props.metadata.orchardSites[parseInt(orchardId)]) })
console.log("Testing OrchardId", this.props.metadata.orchardSites[parseInt(orchardId)])
// parseInt to get access to the array index, (Maybe I need to .ToString() somewhere?
}
}
Which returns..
Uncaught TypeError: Cannot read property 'blocks' of undefined
How can I gain access to this array?
If both the orchardSites (in your jsx and your onClick function) are the same, then you want to access the array using the array index (i in jsx) instead of the orchardSite.code.
This is because OrchardSites[orchardSite.code] can be undefined in certain cases but OrchardSites[parseInt(i)] will not be.
I suggest you change the ‘value’ attribute to “i” instead of orchardSite.code
Cant seem to get this to work:
just want to show each note item on its own line
function EventsTimeline() {
const timelineEvents = [
{
date: 'January',
note: "Words words words"
},
{
date: 'March',
note: 'Scaling open maps for Jakarta flooding — and printed maps'
},
{
date: 'August',
note: 'more stuff ',
},
{
date: 'September',
note: ["Hurricane Harvey recovery maps", "dfdfdfff" ]
},
{
date: 'October',
note: 'Resdfsdfd dgdgdg'
},
{
date: 'November',
note: 'omus omus mos'
},
{
date: 'December',
note: 'rrrrrrs',
}
];
let eventPositionCount = -1;
let eventPositionClass,
eventDescription = null;
const eventElements = timelineEvents.map((event, i) => {
eventPositionCount++;
eventPositionClass = eventPositionCount % 2 === 0 ? 'even' : 'odd';
if (event.note[0].length !== 1 ) {
for (i = 0; i < event.note.length; i++) {
const a = event.note[i]
eventDescription = <p className="txt-s">{a}</p>;
}
} else {
eventDescription = <p className="txt-s">{event.note}</p>;
}
Ive tried many different ways but nothing works.. what am i doing wrong? Currently I can't even get it to recognize the note or date associated with the array
if (Array.isArray(event.note) == true ){
const a = event.note
a.map(function(item) {
eventDescription = {item}
})
} else {
eventDescription = <p className="txt-s">{event.note}</p>;
}
the expected output is just a list..
September
Hurrican Harvey ..etc
dfdfdfff
The way the code works now, it creates a single eventDescription variable, then re-sets that variable to a single element over and over on each loop. Instead, you need that variable to be a list of elements, and not just a single one. Here's what you can do:
function EventsTimeline() {
const eventDescriptionElements = timelineEvents.map((event, i) => {
// here, if the note property isn't already an array, we'll make it one
// otherwise, we'll keep it as-is
// this makes it so that we can always use .map even if it's just one array value
const notes = Array.isArray(event.note) ? event.note : [event.note]
const noteElements = notes.map((noteText, i) => {
// key is required in array elements to silence the react warning
return <p key={i}>{noteText}</p>
})
return (
<p key={i} className="txt-s">
{noteElements}
</p>
)
})
return <div>{eventDescriptionElements}</div>
}
Also, for the even/odd logic you have there: there's no need to create a separate eventPositionCount variable, since the i variable inside of the .map(...) already does that for you.
eventPositionClass = i % 2 === 0 ? 'even' : 'odd';
You should try something like below
function eventsTimeLine() {
timelineEvent.map( event => {
<div>
<p>
{Array.isArray(event.note) ?
event.note.map( words => (
<span> words </span>
: <span> event.note </span>
}
</p>
</div>
});
}
There's an array looking as follows:
[[3,0], [6,0], [2,0], [9,0]....]
I'm trying to create a React/Redux reducer that changes the value of one of the 0s to 1. I click on an element and an action is dispatched. Idx is the index of an element in an array (eg. 0, 1, 2, 3)
export const toggleTile = (idx) => {
return {
type: TOGGLE_TILE,
idx
};
};
The reducer below does not work as I'd like it to be. I just created the skeleton of the conditional statements. If I click on a tile with index 3 (so the fourth tile), it changes the [n,0] to [n,1] for all elements. First of all it should only do if I click any of the tiles, and it should change [n,0] to [n,1] only for the clicked tile so I'm trying to change the 3 in the code below to the index of an 'i' element being mapped.
export default (state = [], action = {}) => {
switch (action.type) {
case LOAD_GRID:
return action.payload || [];
case TOGGLE_TILE:
return state.map((i) => {
if (action.idx === 3) {
return (i[1] === 0
? [i[0], parseInt(i[1], 10) + 1]
: [i[0], parseInt(i[1], 10) - 1]
);
}
return [i[0], i[1]];
});
default:
return state;
}
};
A grid component:
export default class Grid extends Component {
render() {
const mygrid = [];
this.props.inGrid.forEach((r, i) => {
mygrid.push(
<Square
key={i}
idx={i}
sqValue={r}
toggleTile={this.props.toggleTile}
/>
);
});
const { grid } = styles;
return (
<View style={grid}>
{mygrid}
</View>
);
}
}
export default class Square extends Component {
myaction() {
this.props.toggleTile(this.props.idx);
console.log(this.props.idx);
}
render() {
const { square, textStyle, squareActive } = styles;
const { sqValue } = this.props;
return (
<TouchableHighlight
style={[square, sqValue[1] && squareActive]}
onPress={this.myaction.bind(this)}
>
<View>
<Text style={textStyle}>{sqValue[0]},{sqValue[1]}</Text>
</View>
</TouchableHighlight>
);
}
}
Please advise.
There are a number of ways you can do this, with varying degrees of verbosity (due to Redux's insistence on immutability), but here's a pretty straightforward one:
case TOGGLE_TILE:
const nextValue = state[action.idx].slice(); // Make a copy of the tuple to be toggled
nextValue[1] = nextValue[1] === 0 ? 1 : 0; // Toggle it
const nextState = state.slice(); // Make a copy of the state
nextState[action.idx] = nextValue; // Replace the old tuple with the toggled copy
return nextState;
Or:
case TOGGLE_TILE:
const prevValue = state[action.idx];
const nextState = state.slice();
nextState[action.idx] = [ prevValue[0], prevValue[1] === 0 ? 1 : 0 ];
return nextState;
Ok, I'm gonna try and see what we can do with just the following portion of code you shared.
I would like to note that the code presented is not succinct. It would be a great benefit to yourself, your team, as well as anyone here on this site if your code was refactored the more you understand what you need to build.
// So state is just an array of arrays...
var state = [3,0], [6,0], [2,0], [9,0]];
return state.map((i) => { // i => [3,0] or [9,0] !! i is not index !!
// Map is going to iterate over the entire array of arrays.
if (action.idx === 3) {
// action.idx is what comes in from the click.
// Here is where your doing your work.
// If the first element of "i" is zero, then
// return the same array but add 1 to the second element of array.
// so [3,0] or [4,0] should become [3,1] or [4,1] but only for #3 as
// action.idx === 3 says to only change when... Nope, this is not how it will work. You need your exception in the MAP.
return (i[1] === 0 ? [i[0], parseInt(i[1], 10) + 1] : [i[0], parseInt(i[1], 10) - 1]);
}
// ?? Why don't you just return i, as i is each array of numbers.
return [i[0], i[1]];
});
// It seams to me that something like this should work, just plug and play.
// I am assuming a few things here that I will spell out. If they are incorrect, let me know and I'll change it.
// state will be an array of arrays that only contain two numbers each.
// They may or may not be in original order.
// The second element of each array will be either 0 or 1.
var state = [3,0], [6,0], [2,0], [9,0]];
state.map(function(cell){ // call it what you want, you called it "i".
if(cell[0] === action.idx){ // If clicked action index is === to cell[0]
// You could just hard code 3 as you did above, but this makes it dynamic.
// and only changes the cell that was clicked.
cell[1] = cell[1] ? 1 : 0; // if cell[1] is 0, then it is falsey, no need for complex logic. No need to parseInt if they are numbers to begin with. But if you do, then use "+" to change a string to number.
}
return cell;
});
Without notes
var state = [3,0], [6,0], [2,0], [9,0]];
state.map(function(cell){
if(cell[0] === action.idx){
cell[1] = cell[1] ? 1 : 0;
}
return cell;
});
How to do find index==2 in JS?
myObj = {
policy : {
index: 1,
page : "/summer"
},
purchase : {
index: 2,
page : "/sun"
}
}
E.g.
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
if (myObj[key].index === 2)
console.log("Found.");
}
}
How to do this in JS more efficiently?
Javascript-wise I'd use the Object.keys() function:
Object.keys(myObj).forEach(key => {
if (myObj[key].index === 2) {
console.log("Found.");
}
});
Because it removes the need to check myObj.hasOwnProperty(key).
If you want to stop the search when one was found:
Object.keys(myObj).some(key => myObj[key].index === 2);
Use Array.find:
Object.keys(myObj).find(k => myObj[k].index === 2)
This will return the key in which the match occurred.