const MyComponent = () => {
const history = useHistory();
let [val, setVal] = useState(0);
useEffect(() => {
trackPageView();
history.listen(trackPageView);
}, []);
function trackPageView() {
console.log('hello: ' + val);
setVal(val + 1);
}
}
useEffect runs once and registers trackPageView with history.listen. As the history changes, trackPageView is called as expected but val always has the same start value of '0'. (The above code is based on the code in this article.)
In contrast, this code does a similar thing...
var a = 1;
function g(f) {
return () => f();
}
let func = g(() => console.log('a: ' + a));
func();
a = 2;
func();
...but a's changing value is reflected in the output. https://jsfiddle.net/s3e250da/
So, what accounts for the difference in behaviour?
Related
I have 5 functions: func1(), func2(), func3(), func4(), func5(). I need to implement the compositionFunc() function, which can take any number of functions as arguments, and create a composition from them. The compositionFunc() function takes my 5 functions as arguments. The compositionFunc() function returns a function that takes its initial value as an argument. This nested function successively passing through an array of functions with each iteration returns the result of calling the accumulated value of the current function-argument. The result of one function can be passed as an argument to another function. How can i do this?
const func1 = (arg1) => {
return arg1;
};
const func2 = (arg2) => {
return arg2;
};
const func3 = (arg3) => {
return arg3;
};
const func4 = (arg4) => {
return arg4;
};
const func5 = (arg5) => {
return arg5;
};
const compositionFunc = () => {
...
};
you can define a function like this
const pipe = (...functions) => args => functions.reduce((res, f) => f(res), args)
const combine = (...functions) => args => functions.reduceRight((res, f) => f(res), args)
const plus1 = x => x + 1
const double = x => x * 2
const pipeFunction = pipe(plus1, double)
const combineFunction = combine(plus1, double)
console.log(combineFunction(1)) // (1 * 2) + 1
console.log(pipeFunction(1)) // (1 + 1) * 2
A simple reduce can accomplish that:
function pipe(input, ...func) {
return func.reduce((a, f) => f(a), input);
}
You pass it an initial value + chain of functions.
Example:
function f1(val) {
return val + 1;
}
function f2(val) {
return val * 10;
}
console.log(pipe(2, f1, f2)); //=> 30
I am challenging myself by writing a simple JavaScript callback hell to grasp the concept without using the widely spread setTimeout function examples on the net.
I did something wrong and haven't succeeded yet to nest my callbacks and return the final result.
const multiplication = (a, b) => {
let multiply = a * b;
console.log(`${multiply} from multiplication`);
return multiply;
};
const addition = (multiply, n1) => {
let add = multiply + n1;
console.log(`${add} from addition`);
return division(add);
};
const division = (add) => {
let div = add / 2;
console.log(`${div} from division`);
return div;
};
console.log(
multiplication(10, 20, () => {
addition(multiply, 100, () => {
division(add);
});
})
);
If you want to use callbacks, then you'll have to define them as arguments to your functions, and also to call them at the end instead of returning.
Due to how callback control flow works, you won't be able to do
console.log(
multiplication(10, 20, () => {
...
Rather, you'll have to log in the innermost defined callback to get to the final result.
const multiplication = (a, b, callback) => {
let multiply = a * b;
console.log(`${multiply} from multiplication`);
callback(multiply);
};
const addition = (multiplyResult, n1, callback) => {
let add = multiplyResult + n1;
console.log(`${add} from addition`);
callback(add);
};
const division = (add, callback) => {
let div = add / 2;
console.log(`${div} from division`);
callback(div);
};
multiplication(10, 20, (multiplyResult) => {
addition(multiplyResult, 100, (addResult) => {
division(addResult, (divisionResult) => {
console.log('innermost', divisionResult);
});
});
})
I have an exercise to make a function executeFunctions which takes as arguments a list of async functions and an argument, e.g. number.
The functions have to happen one after another, so if fun1 ends, fun2 needs to start with the value which was returned from fun1.
The problem is that I can't use async and await. I wanted to do it using reduce, but I guess that it wants to execute const res1 and go further before it returns a value (because of setTimeout).
Is there any way to do it without async and await?
const fun1 = function(value) {
return setTimeout(() => value*2, 3000)
}
const fun2 = function(value) {
return setTimeout(() => value*4, 3000)
}
const cb2 = (value) => {
return value*10
}
const executeFunctions = (funTab, cb) => (n) => {
const res1= funTab[0](n)
console.log(res1)
const resReduce = funTab.reduce((prev,curr) => {
const res2 = curr(prev)
return prev+res2
}, res1)
return cb(resReduce)
};
executeFunctions([fun1,fun2], cb2)(2)
We can use Promise-chaining:
const fun1 = function(value) {
return Promise.resolve(value * 2);
}
const fun2 = function(value) {
return Promise.resolve(value * 2);
}
const fun3 = function(value) {
return Promise.resolve(value * 2);
}
const executeFunctions = (funcList) => (n) => {
let chain = Promise.resolve(n); // initial value
for (let i = 0; i < funcList.length; i++) {
chain = chain.then(funcList[i]); // keep chaining
}
return chain; // last promise
};
const main = () => {
// we need to wait for the last promise in order to print the result
executeFunctions([fun1, fun2, fun3])(2).then(x => console.log('solution is:', x));
}
main() // prints: "solution is: 16"
or, we can also use a modified version of the suggested reduce solution, by changing the implementation of executeFunctions as follows (the rest of the code should remain as in the previous snippet):
const executeFunctions = (funcList) => (n) => {
const init = Promise.resolve(n);
const res = funcList.reduce((p, c) => {
return p.then(c)
}, init);
return res;
};
This function should have two parameters: a function and a value. It should call the argument function with the value two times. If the callback function produces the same result twice, it should return the result of the function call, otherwise, it should return the string 'This function returned inconsistent results'
const checkThatTwoPlusTwoEqualsFourAMillionTimes = () => {
for(let i = 1; i <= 1000000; i++) {
if ( (2 + 2) != 4) {
console.log('Something has gone very wrong :( ');
}
}
};
const addTwo = num => num + 2;
const timeFuncRuntime = funcParameter => {
let t1 = Date.now();
funcParameter();
let t2 = Date.now();
return t2 - t1;
};
// Write your code below
const time2p2 = timeFuncRuntime(checkThatTwoPlusTwoEqualsFourAMillionTimes);
const checkConsistentOutput(func, val) => {
let checkOne = func(val);
let checkTwo = func(val);
if (checkOne === checkTwo){
return checkOne;
} else {
return 'This function returned inconsisent results'
}
}
I'm getting the error SyntaxError: Missing initializer in const declaration. please help me understand.
I'm seeing an error in the last const declaration that appears to be because it's missing an '='.
const checkConsistentOutput(func, val) => {
...
}
const checkConsistentOutput = (func, val) => {
...
}
I have a simple code which iterates through an array and logs them in the interval of 1000 ms as such :
const arr = [1, 2, 3];
let i = 0;
const choice = () => {
const interval = setInterval(() => {
console.log(arr[i++ % arr.length]);
if (i === 8) {
clearInterval(interval);
}
}, 1000);
};
choice();
Upon introducing React state into the code, the whole thing goes absolutely mad and starts counting out of the interval to a point that I reach almost infinite loop although the simple console.log instead of react state works fine.
const [ele, setEle] = React.useState(null);
const arr = [1, 2, 3];
let i = 0;
const choice = () => {
const interval = setInterval(() => {
setEle(arr[i++ % arr.length]);
if (i === 8) {
clearInterval(interval);
}
}, 1000);
};
choice();
return(
<h1>{ele}</h1>
)
I wonder how I can achieve the effect using the state with the current code.
https://codesandbox.io/s/condescending-dubinsky-kbl5m
With your current implementation, every render of the component is initializing a new interval, so you get lots of intervals running simultaneously, each of which initiate even more intervals. Use setTimeout instead, so that every render only initializes exactly one action to take place in the future.
Since it's the index of the array that changes each time, consider using that as state instead:
const App = () => {
const [i, setI] = React.useState(0);
const arr = [1, 2, 3];
if (i !== 8) {
setTimeout(() => {
setI(i + 1);
}, 1000);
}
return (
<div className="App">
<h1>{arr[i % arr.length]}</h1>
</div>
);
}
ReactDOM.render(
<App />,
document.body
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
I found the other answer somewhat unsatisfying because it requires a new timeout to be generated each time. It is possible to set up a single setInterval which requires a useRef to get an up-to-date setI into the timer. The empty useEffect dependencies ensures the setInterval is not re-run on each state update.
const App = () => {
const [i, setI] = React.useState(0);
const timer = React.useRef();
timer.current = () => setI(i+1);
React.useEffect(() => {
const interval = setInterval(() => {
timer.current();
}, 1000);
return () => {
clearInterval(interval)
};
}, [])
return <div>{i}</div>;
}