const columns = ['task', 'category'];
for (const text in columns) {
const [text, setText] = useState();
}
I wish to create multiple useState things using a loop but to join things together seem to be a problem.
What I want it to do is to create consts: task, setTask | category, setCategory
Check this out:
https://reactjs.org/docs/hooks-rules.html
Explicitly mentions:
Only Call Hooks at the Top Level Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top
level of your React function. By following this rule, you ensure that
Hooks are called in the same order each time a component renders.
That’s what allows React to correctly preserve the state of Hooks
between multiple useState and useEffect calls. (If you’re curious,
we’ll explain this in depth below.)
const [text, setText] = ... is just a deconstructing assignment, you can store the result of useState(..) (an array with two elements) however you want, for example:
const columns = ['task', 'category'];
const state = {};
for (const prop of columns) {
const resultArr = useState();
state[prop] = {
val: resultArr[0],
fn: resultArr[1]
};
}
And you can use the results like this:
<button onClick={() => state.task.fn('new value')}>{state.task.val}</button>
But as #EmileBergeron mentioned, there doesn't seem to be any reason to use useState like this. There is definitely a more conventional way of solving the problem that you're facing.
Under the hood, useState relies on a linked-list-type nested data structure in the fiber. Each time you call useState that structure is "unwrapped" to get the data associated with the next hook call.
This is a good article that goes into depth about how hooks work.
In theory, so long as your loop always executes the same number of times, you could use a hook inside of the loop. HOWEVER, as other people have said here, this is not a standard way of doing things and is not advisable.
const columns = ['task', 'category'];
columns.map( (column, index) => {
let col = column;
let setCol = column+index;
[col, setCol] = useState(false)
})
This way it will give unique state for each iteration of elements in the array columns
const [uploadAnimal, setAnimal] = useState(['cat']);
const loopFun = () => {
const arr = uploadAnimal
for (let i = 0; i < 10; i++) {
arr.push('DOG')
setUploadCount([...arr])
} };
Related
const semvers = ["5.100.0-rc.0", "5.97.3", "5.97.1"];
const newRecord = "5.97.2";
Given the above test data, I wish to insert newRecord into the right order, defined/sorted by semver package.
result = ["5.100.0-rc.0", "5.97.3", "5.97.2", "5.97.1"];
Below is my attempt which gave me the correct result
const semvers = ["5.100.0-rc.0", "5.97.3", "5.97.1"];
const newRecord = "5.97.2";
const indexResult = R.findIndex(x => semver.lt(x, newRecord))(semvers);
const result = R.insert(indexResult, newRecord, semvers)
Then, i was wondering if I can replace x with R.__, so i attempted below
const indexResult = R.findIndex(semver.lt(R.__, newRecord))(semvers);
I had the impression that R.__ referring to the arguments that was gonna passed but seems like it's not, or it was simply due to the fact that semver.lt is not a curried function and hence couldn't comprehend R.__?
R.__ works with Ramda functions or functions curried with Ramda e.g.,
const semvers = ["5.100.0-rc.0", "5.97.3", "5.97.1"];
const newRecord = "5.97.2";
const findVer = R.curryN(2, semver.lt)(R.__, newRecord);
const indexResult = R.findIndex(findVer, semvers);
const result = R.insert(indexResult, newRecord, semvers);
My preferred option would have been: R.flip(semver.lt)(newRecord) unfortunately semver.lt arity is 3 (third argument is a loose parameter) so R.flip doesn't work straight out of the box.
With R.partialRight you could supply the last two arguments (including that undocumented (?) loose parameter):
const findVer = R.partialRight(semver.lt, [newRecord, false]);
But honestly what you had originally is fine.
I have a massive data that I can obtain only using third party hooks, for example:
const data1 = useSmthHook(1) where 1 - index;
Lets imagine that we need an object of 100 values:
const data1 = useSmthHook(1)
const data2 = useSmthHook(2)
const data3 = useSmthHook(3)
...
const data100 = useSmthHook(100)
It would be great to get as final result object with all 100 values.
Solution with loop doesn't work because of hooks can't be triggered inside loops.
//does not work!
for (var i = 1; i < 101; i++) {
const data = useSmthHook(i)
....
}
I have only one idea (im sure a bad one) that works: create empty array, rendering 100 components, in Item component logic: while render get hook value => update global state with this data. So every new Item render causes +1 row (update) global state. 100 updates and 100 re-reneders.
How can I filter table data on the basis of a range of dates?
setting filter to date column here:
const tableInstance = useRef(null);
const filterTable = (dates) => {
if (tableInstance.current) {
tableInstance.current.setFilter('session_date', dates);
}
};
onClick functionality is here:
const handleFilter = () => {
setSessionsData(data);
if (sessionsData) {
const dateArray = getDates(
moment(fromDate).format('L'),
moment(toDate).format('L')
);
filterTable(dateArray);
}
};
Add this filter to your respective column object
{
id: 'your_column_id',
accessor: 'your_accessor',
filter: (rows, id, filterValue) => {
return rows.filter(
(row) =>
filterValue.length <= 0 ||
!filterValue ||
filterValue.includes(row.values[id])
);
}
}
Here the filterValue contains the array containing all the possible matches that are required i.e dateArray (all dates from 'fromDate' to 'toDate') in your case.
If hope u are good with react concept of hooks or if u need help please follow the below link
https://reactjs.org/docs/hooks-reference.html
https://www.digitalocean.com/community/tutorials/how-to-call-web-apis-with-the-useeffect-hook-in-react
Now to your question the approach you must take.
There are two states value which u have put filter on i.e, your date_range.
You can just pass a event on filter click to update the states for date_range
Where you will add hooks to set value in table like given below,
const [list, setList] = useState([]);
useEffect(() => {
fetch('http://localhost:3333/list')
.then(data => {
setList(data.json);
})
})
}, [])
Also, One more thing to keep in mind is not to blindly call API on any state changes i.e, is there any value that have really changed from original one in state here you must know the concept of pure component to prevent you component from blindly calling the API, below is the link to use pure component in react,
https://reactjs.org/docs/react-api.html
https://www.digitalocean.com/community/tutorials/five-ways-to-convert-react-class-components-to-functional-components-with-react-hooks
I've initialized a state that is an array, and when I update it my component does not re-render. Here is a minimal proof-of-concept:
function App() {
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
console.log("rendering...");
return (
<div className="App">
{numbers.map(number => (
<p>{number}</p>
))}
<input
type="text"
value={numbers[0].toString()}
onChange={newText => {
let old = numbers;
old[0] = 1;
setNumbers(old);
}}
/>
</div>
);
}
Based on this code, it seems that the input should contain the number 0 to start, and any time it is changed, the state should change too. After entering "02" in the input, the App component does not re-render. However, if I add a setTimeout in the onChange function which executes after 5 seconds, it shows that numbers has indeed been updated.
Any thoughts on why the component doesn't update?
Here is a CodeSandbox with the proof of concept.
You're calling setNumbers and passing it the array it already has. You've changed one of its values but it's still the same array, and I suspect React doesn't see any reason to re-render because state hasn't changed; the new array is the old array.
One easy way to avoid this is by spreading the array into a new array:
setNumbers([...old])
You need to copy numbers like so let old = [...numbers];
useState doesn't update the value only if it has changed so if it was 44 and it became 7 it will update. but how can it know if an array or object have changed. it's by reference so when you do let old = numbers you are just passing a reference and not creating a new one
Others have already given the technical solution. To anyone confused as to why this happens, is because setSomething() only re renders the component if and only if the previous and current state is different. Since arrays in javascript are reference types, if you edit an item of an array in js, it still doesn't change the reference to the original array. In js's eyes, these two arrays are the same, even though the original content inside those arrays are different. That's why setSomething() fails do detect the changes made to the old array.
Note that if you use class components and update the state using setState() then the component will always update regardless of whether the state has changed or not. So, you can change your functional component to a class component as a solution. Or follow the answers provided by others.
You can change state like this
const [state, setState] = ({})
setState({...state})
or if your state is Array you can change like this
const [state, setState] = ([])
setState([...state])
I was working on an array that had objects in it and I tried few of the above.
My useState is :
const [options, setOptions] = useState([
{ sno: "1", text: "" },
{ sno: "2", text: "" },
{ sno: "3", text: "" },
{ sno: "4", text: "" },
]);
Now I want to add more options with blank field on a click of a button I will use the following way to achieve my purpose:
<button
onClick={() => {
setOptions([...options, { sno: newArray.length + 1, text: "" }]);
}}
>
This solved my problem and I was able to re render the component and added an object to the array.
introduces an array of the component that is not the one of the hook. for instance:
const [numbers, setNumbers] = useState([0, 1, 2, 3]);
var numbersModify = []; //the value you want
and at the end:
setNumbers(numbersModify)
modify this numbersModify, when the hook refreshes it will return to 0 numbersModify and the hook will keep the state. Therefore, the problem of not seeing the changes will be eliminated.
:D
//define state using useState hook
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
//copy existing numbers in temp
let tempNumbers = [...numbers];
// modify/add no
tempNumbers.push(4);
tempNumbers[0] = 10;
// set modified numbers
setNumbers(tempNumbers);
I dont have any proud of this but it works
anotherList = something
setSomething([])
setTimeout(()=>{ setSomething(anotherList)},0)
useState is a React hook which provides functionality of having State in a functional component.
Usually it informs React to re-render the component whenever there is change in useState variables.
{
let old = numbers;
old[0] = 1;
setNumbers(old);
}
In the above code since you are referring to the same variable it stores the reference not the value hence React doesn't know about the latest changes as the reference is same as previous.
To overcome use the below hack, which will not copy the reference instead it's a deep copy(copies the values)
{
let old = JSON.parse(JSON.stringify(numbers));
old[0] = 1;
setNumbers(old);
}
Happy coding :)
I have been experimenting with RxJS for two weeks now, and although I love it in principle I just cannot seem to find and implement the correct pattern for managing state. All articles and questions appear to agree:
Subject should be avoided where possible in favor of just pushing state through via transformations;
.getValue() should be deprecated entirely; and
.do should perhaps be avoided except for DOM manipulation?
The problem with all such suggestions is that none of the literature appears to directly say what you should be using instead, besides "you'll learn the Rx way and stop using Subject".
But I cannot find a direct example anywhere that specifically indicates the correct way to perform both additions and removals to a single stream/object, as the consequence of multiple other stream inputs, in a stateless and functional manner.
Before I get pointed in the same directions again, problems with uncovered literature are:
The Introduction to Reactive Programming You've been missing: great starting text, but does not specifically address these questions.
The TODO example for RxJS comes with React and involves explicit manipulation of Subjects as proxies for React Stores.
http://blog.edanschwartz.com/2015/09/18/dead-simple-rxjs-todo-list/ : explicitly uses a state object for addition and removal of items.
My perhaps 10th rewrite of the standard TODO follows - My prior iterations covered include:
starting with a mutable 'items' array - bad as state is explicit and imperatively managed
using scan to concatenate new items to an addedItems$ stream, then branching another stream where the removed items were deleted - bad as the addedItems$ stream would grow indefinitely.
discovering BehaviorSubjectand using that - seemed bad since for each new updatedList$.next() emission, it requires the previous value to iterate, meaning that Subject.getValue() is essential.
trying to stream the result of the inputEnter$ addition events into filtered removal events - but then every new stream creates a new list, and then feeding that into the toggleItem$ and toggleAll$ streams means that each new stream is dependent on the previous, and so causing one of the 4 actions (add, remove, toggle item or toggle all) requires the whole chain to be unnecessarily run through again.
Now I have come full circle, where I am back to using both Subject (and just how is it supposed to be successively iterated upon in any way without using getValue()?) and do, as show below. Myself and my colleague agree this is the clearest way, yet it of course seems the least reactive and most imperative. Any clear suggestions on the correct way for this would be much appreciated!
import Rx from 'rxjs/Rx';
import h from 'virtual-dom/h';
import diff from 'virtual-dom/diff';
import patch from 'virtual-dom/patch';
const todoListContainer = document.querySelector('#todo-items-container');
const newTodoInput = document.querySelector('#new-todo');
const todoMain = document.querySelector('#main');
const todoFooter = document.querySelector('#footer');
const inputToggleAll = document.querySelector('#toggle-all');
const ENTER_KEY = 13;
// INTENTS
const inputEnter$ = Rx.Observable.fromEvent(newTodoInput, 'keyup')
.filter(event => event.keyCode === ENTER_KEY)
.map(event => event.target.value)
.filter(value => value.trim().length)
.map(value => {
return { label: value, completed: false };
});
const inputItemClick$ = Rx.Observable.fromEvent(todoListContainer, 'click');
const inputToggleAll$ = Rx.Observable.fromEvent(inputToggleAll, 'click')
.map(event => event.target.checked);
const inputToggleItem$ = inputItemClick$
.filter(event => event.target.classList.contains('toggle'))
.map((event) => {
return {
label: event.target.nextElementSibling.innerText.trim(),
completed: event.target.checked,
};
})
const inputDoubleClick$ = Rx.Observable.fromEvent(todoListContainer, 'dblclick')
.filter(event => event.target.tagName === 'LABEL')
.do((event) => {
event.target.parentElement.classList.toggle('editing');
})
.map(event => event.target.innerText.trim());
const inputClickDelete$ = inputItemClick$
.filter(event => event.target.classList.contains('destroy'))
.map((event) => {
return { label: event.target.previousElementSibling.innerText.trim(), completed: false };
});
const list$ = new Rx.BehaviorSubject([]);
// MODEL / OPERATIONS
const addItem$ = inputEnter$
.do((item) => {
inputToggleAll.checked = false;
list$.next(list$.getValue().concat(item));
});
const removeItem$ = inputClickDelete$
.do((removeItem) => {
list$.next(list$.getValue().filter(item => item.label !== removeItem.label));
});
const toggleAll$ = inputToggleAll$
.do((allComplete) => {
list$.next(toggleAllComplete(list$.getValue(), allComplete));
});
function toggleAllComplete(arr, allComplete) {
inputToggleAll.checked = allComplete;
return arr.map((item) =>
({ label: item.label, completed: allComplete }));
}
const toggleItem$ = inputToggleItem$
.do((toggleItem) => {
let allComplete = toggleItem.completed;
let noneComplete = !toggleItem.completed;
const list = list$.getValue().map(item => {
if (item.label === toggleItem.label) {
item.completed = toggleItem.completed;
}
if (allComplete && !item.completed) {
allComplete = false;
}
if (noneComplete && item.completed) {
noneComplete = false;
}
return item;
});
if (allComplete) {
list$.next(toggleAllComplete(list, true));
return;
}
if (noneComplete) {
list$.next(toggleAllComplete(list, false));
return;
}
list$.next(list);
});
// subscribe to all the events that cause the proxy list$ subject array to be updated
Rx.Observable.merge(addItem$, removeItem$, toggleAll$, toggleItem$).subscribe();
list$.subscribe((list) => {
// DOM side-effects based on list size
todoFooter.style.visibility = todoMain.style.visibility =
(list.length) ? 'visible' : 'hidden';
newTodoInput.value = '';
});
// RENDERING
const tree$ = list$
.map(newList => renderList(newList));
const patches$ = tree$
.bufferCount(2, 1)
.map(([oldTree, newTree]) => diff(oldTree, newTree));
const todoList$ = patches$.startWith(document.querySelector('#todo-list'))
.scan((rootNode, patches) => patch(rootNode, patches));
todoList$.subscribe();
function renderList(arr, allComplete) {
return h('ul#todo-list', arr.map(val =>
h('li', {
className: (val.completed) ? 'completed' : null,
}, [h('input', {
className: 'toggle',
type: 'checkbox',
checked: val.completed,
}), h('label', val.label),
h('button', { className: 'destroy' }),
])));
}
Edit
In relation to #user3743222 very helpful answer, I can see how representing state as an additional input can make a function pure and thus scan is the best way to represent a collection evolving over time, with a snapshot of its previous state up to that point as an additional function parameter.
However, this was already how I approached my second attempt, with addedItems$ being a scanned stream of inputs:
// this list will now grow infinitely, because nothing is ever removed from it at the same time as concatenation?
const listWithItemsAdded$ = inputEnter$
.startWith([])
.scan((list, addItem) => list.concat(addItem));
const listWithItemsAddedAndRemoved$ = inputClickDelete$.withLatestFrom(listWithItemsAdded$)
.scan((list, removeItem) => list.filter(item => item !== removeItem));
// Now I have to always work from the previous list, to get the incorporated amendments...
const listWithItemsAddedAndRemovedAndToggled$ = inputToggleItem$.withLatestFrom(listWithItemsAddedAndRemoved$)
.map((item, list) => {
if (item.checked === true) {
//etc
}
})
// ... and have the event triggering a bunch of previous inputs it may have nothing to do with.
// and so if I have 400 inputs it appears at this stage to still run all the previous functions every time -any- input
// changes, even if I just want to change one small part of state
const n$ = nminus1$.scan...
The obvious solution would be to just have items = [], and manipulate it directly, or const items = new BehaviorSubject([]) - but then the only way to iterate on it appears to be using getValue to expose the previous state, which Andre Stalz (CycleJS) has commented on in the RxJS issues as something that shouldn't really be exposed (but again, if not, then how is it usable?).
I guess I just had an idea that with streams, you weren't supposed to use Subjects or represent anything via a state 'meatball', and in the first answer I'm not sure how this doesn't introduce mass chained streams which are orphaned/grow infinitely/have to build on each other in exact sequence.
I think you already found a good example with : http://jsbin.com/redeko/edit?js,output.
You take issue with the fact that this implementation
explicitly uses a state object for addition and removal of items.
However, thas is exactly the good practice you are looking for. If you rename that state object viewModel for example, it might be more apparent to you.
So what is state?
There will be other definitions but I like to think of state as follows:
given f an impure function, i.e. output = f(input), such that you can have different outputs for the same input, the state associated to that function (when it exists) is the extra variable such that f(input) = output = g(input, state) holds and g is a pure function.
So if the function here is to match an object representing a user input, to an array of todo, and if I click add on a todo list with already have 2 todos, the output will be 3 todos. If I do the same (same input) on a todo list with only one todo, the output will be 2 todos. So same input, different outputs.
The state here that allows to transform that function into a pure function is the current value of the todo array. So my input becomes an add click, AND the current todo array, passed through a function g which give a new todo array with a new todo list. That function g is pure. So f is implemented in a stateless way by making its previously hidden state explicit in g.
And that fits well with functional programming which revolves around composing pure functions.
Rxjs operators
scan
So when it comes to state management, with RxJS or else, a good practice is to make state explicit to manipulate it.
If you turn the output = g(input, state) into a stream, you get On+1 = g(In+1, Sn) and that's exactly what the scan operator does.
expand
Another operator which generalizes scan is expand, but so far I had very little use of that operator. scan generally does the trick.
Sorry for the long and mathy answer. It took me a while to get around those concepts and that's the way I made them understandable for me. Hopefully it works for you too.