How can I set State based on other state in functional component? - javascript

I build a simple todo app with a react with an array of todos:
const todos = [description: "walk dog", done: false]
I use the following two states:
const [alltodos, handleTodos] = useState(todos);
const [opencount, countOpen] = useState(alltodos.length);
This is the function which counts the open todos:
const countTodos = () => {
const donetodos = alltodos.filter((item) => {
return !item.done;
});
countOpen(donetodos.length);
};
When I try to add a new todo, I also want to update the opencount state with the countTodos function.
const submitTodo = (event) => {
event.preventDefault();
const data = {
description: todo,
done: false,
};
handleTodos([...alltodos, data]);
console.log(alltodos);
countTodos();
};
This does not work as expected, the when I run console.log(alltodos) it will show an empty array. The function itself works, but it seems to have a "delay", I guess based on the async nature of the useState hook.
I tried to pass the countTodos function as callback like this, since I have seen something similar in class based components.
handleTodos([...alltodos, data], () => {
countTodos();
});
I get the following error:
Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().
How can I solve this problem? What is the best way for me to update the state based on another state?

I think you should useEffect, (clearly stated on the log message ). this is an example :
useEffect(()=>{
const donetodos = alltodos.filter((item) => {
return !item.done;
});
countOpen(donetodos.length);
//countTodos();
},[alltodos]];
You can refer to the documentation : https://reactjs.org/docs/hooks-effect.html
Here is an example : https://codesandbox.io/s/react-hooks-useeffect-forked-4sly8

Related

React: Async from class to functional

I just started learning React and so far I'm liking it. But the problem is that most of the tutorials are old and still using old states instead of hooks.
Now when I'm following a tutorial, I'm trying to convert the code I see into hooks. For now I'm really stuck and could use some help for this code. I'm trying to convert the code bellow for a functionnal component. I tried using useEffect but it didn't work.
handleChangeOrder(oldIndex, newIndex){
if (oldIndex == newIndex) {return;}
const [todos] = this.state;
let newSequence =[];
todos.forEach((todos,index)=> {
newSequence.push({id: todo.id, order: index + 1 });
});
}
-------------------------------------------
static changeTodoOrderURL(){
return apiDomain + "api/todo/reorder";
}
----------------------------------------
async changeTodoOrder(order){
const url = UrlService.changeTodoOrderUrl();
try{
const response = await HttpService.post(url,{order});
return response.data;
} catch (error){console.error("Not able to change order of the todos")};
}
What I tried so far in converting it :
function handleChangeOrder(oldIndex, newIndex) {
if (oldIndex == newIndex) {
return;
}
const todos = Array.from(todolist);
let newSequence = [];
todos.forEach((todos, index) => {
newSequence.push({id: todo.id, order: index + 1 });
});
useEffect(()=>{
axios.post('http://localhost:8000/api/todo/reorder')
.then(response=>{
setList(response.data);
console.log(response)
})
.catch(err => console.log(err));
},[])
}
I'm struggling mainly with async changeTodoOrder..
You don't need to use useEffect to send API requests based on event change, useEffect used to send API requests/perform side effect based on state change or on the mounting of the component.
Also, you can't call the hooks inside a function, by using it this way you are breaking the rules of hooks.
please review the rules of Hooks here
based on your code above from the class component you can use it as it is without using useEffect

react component not running the cleanup function on unmount. Working with firestore

I have a react component useEffect hook that looks like the following. I am working with firestore. I am trying to remove a value from an array in firestore when the component unmounts. However, the value is not getting removed. I tried running the firestore query in the cleanup function independently to see if that query was the problem, but it's working fine independently. It's just not getting executed when it's inside the cleanup function. I THINK the problem is that my cleanup function at the end of the useEffect hook is not getting called when the component unmounts(for example when I close the window). does anyone know what I may be doing wrong? Thank you for your help in advance
useEffect(() => {
.......
return () => {
fire.firestore().collection("ActiveUsers").doc(utilvar.teacherID).get().then((snapshot) => {
var docref = snapshot.ref;
return docref.update({
active_users : fieldValue.arrayRemove({id: currentUser.uid, name: displayName})
})
})
};
}, []);
From my observation. utilvar.teacherID might not be ready as at the time your component got mounted.
So you may want to add it to the dependable array.
Your useEffect should therefore look something like this :
useEffect(() => {
.......
return () => {
fire.firestore().collection("ActiveUsers").doc(utilvar.teacherID).get().then((snapshot) => {
if(snapshot.exist){
return snapshot.ref.update({
active_users : fieldValue.arrayRemove({id: currentUser.uid, name: displayName})
}).catch(err=>err);
})
};
}, [utilvar.teacherID]);
I added the catch as it a promise being returned. It must be handled appropriately irrespective of where it's being used.

Difference between arrow functions and normal functions in React Native

So I've always thought of arrow functions to be a new better and version of normal js functions until today. I was following a tutorial on how to use firestore to store data when I came across a problem that made realise the two are different and work in a weird way.
His code looked like this:
//component
function Todos() {
const [ todo, setTodo ] = useState('');
const ref = firestore().collection('todos');
// ...
async function addTodo() {
await ref.add({ title: todo, complete: false});
setTodo('');
}
// ...
}
My code looked like this:
//component
const Todos = () => {
const ref = firestore().collection('todos');
const [todo, setTodo] = useState('');
const addTodo = async () => {
const res = await ref.add({ title: todos, complete: false });
setTodo('');
};
};
Now his version worked, while mine didn't.
After changing my code to look like his, it worked. But the weird thing i realised was this: after clicking on the button that invoked that function for the first time (with his function), i changed the code back to mine and it worked the second time. I did some reading on the two functions but i couldn't get to reasoning behind why this happened.
Arrow functions and normal function are not equivalent.
Here is the difference:
Arrow function do not have their own binding of this, so your this.setState refer to the YourClass.setState.
Using normal function, you need to bind it to the class to obtain Class's this reference. So when you call this.setState actually it refer to YourFunction.setState().
Sample Code
class FancyComponent extends Component {
handleChange(event) {
this.setState({ event }) // `this` is instance of handleChange
}
handleChange = (event) => {
this.setState({ event }) // `this` is instance of FancyComponent
}
}

Redux State Mutation When Dispatch an Action

I have a table like
When you edit the quantity using this onChange
onChange={this.handleInputChange.bind(null, cellInfo)}
I run the below code
handleInputChange = (cellInfo, event) => {
let data = { ...this.props.Data };
data[cellInfo.index][cellInfo.column.id] = parseInt(event.target.value);
this.props.APISummaryData(data);
};
Goal being first get the data in the store, then reflect the value you changed and then update it with action this.props.APISummaryData(data); and this.props.APISummaryData({ ...data }); both give same State mutation error.
Here's the reducer
case types.API_SUMMARY_DATA:
return {
...state,
Summary: {
...state.Summary,
Data: action.Summary
}
};
If I manually dispatch an action within Redux inside DevTools doing
{
type: 'API_SUMMARY_DATA',
Summary: [
{
cusip: '019I',
quantity: 55,
}
]
}
This is the action
export const APISummaryData = Summary => ({ type: types.API_SUMMARY_DATA, Summary });
I don't get any error and data gets updated. I am so puzzled where in this scheme I mutate the state?
Note: it is possible I am not sharing some code that's important to take a look here, so please let me know and I'll share it.
exact error
I assume that you're using configureStore() from Redux Starter Kit, which sets up a mutation checking middleware by default. Good! This means that the mutation checker is doing its job correctly.
These lines right here are mutating:
let data = { ...this.props.Data };
data[cellInfo.index][cellInfo.column.id] = parseInt(event.target.value);
That's because the {...} object spread operator does a shallow copy, not a deep copy. This is a very common mistake.
I personally would recommend dispatching an action that looks like:
{type: "API_SUMMARY_DATA", payload: {index, columnId, inputValue}}
and then use the reducer to do all the updating.
Also, if you are using Redux Starter Kit, you can use our createReducer() function to write "mutative" code in the reducer that actually does immutable updates.

setState doesn't update all state properties after call

The thing is, that I use setState to update state.n and state.data. I want to call setState onClick, using handler. After calling setState, I perform a callback console.log to watch the changed state properties, but only state.data is changed, not state.n.
handler(e) {
this.setState((state) => {
let arr = state.data;
arr.push(fakeTableData[state.n+1]);
this.setState({n: state.n+1, data: arr});
}, console.log.bind(null, this.state.n));
}
After one handler used, I expect to see state.n incremented, but I had only state.data changed, not state.n. And I recieve a warning, like this: "Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback." I've read a tutorial on the main react site, but there not enough info.
let's try let arr = [...state.data]
You should not use setState inside function that sets state. That is why you got an error. Instead you should return state. Try this:
handler = (e) => {
this.setState((state) => {
let arr = state.data;
arr.push(fakeTableData[state.n+1]);
return {n: state.n+1, data: arr};
}, () => { console.log(this.state.n) });
}

Categories

Resources