Reputation: 157
I have a functional component with a useState hook. Its values are coming from my redux store and I want to update the state with the are store state every time a dispatch an action.
Right now I have hardcoded an array that the useState starts with. I want to be able to push in new elements in the array via redux and have react re-render the new content.
See code below:
import React, { useState } from "react";
import "./style.scss";
import { FormEquation } from "../calc/interfaces/form";
import { FlowrateCalc } from "../calc/calculators/FlowrateCalc";
import { useSelector } from "react-redux";
import { RootState } from "../state/reducers";
import { ValveKvsCalc } from "../calc/calculators/ValveKvsCalc";
function Calculator() {
const state = useSelector((state: RootState) => state.calc);
// const state = [
// {
// ...FlowrateCalc,
// priorityList: FlowrateCalc.inputs.map((input) => input.name),
// },
// {
// ...ValveKvsCalc,
// priorityList: ValveKvsCalc.inputs.map((input) => input.name),
// },
// ];
// Usestate is run once after render and never again. How do I update this state whenever new content arrived from "useSelector"??
const [formsEQ, setformsEQ] = useState<FormEquation[]>([...state]);
const inputsHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
// Copy form and get index of affected form
const formCopy = formsEQ.slice();
const [formName, inputFieldName] = e.target.name.split("-");
const formIndex = formsEQ.findIndex((formEQ) => formEQ.name === formName);
if (formIndex === -1) return;
// if anything other than a number or dot inputted, then return
// meTODO: if added number then trying to delete all numbers will stop!
const isInputNum = e.target.value.match(/[0-9]*\.?[0-9]*/);
if (!isInputNum || isInputNum[0] === "") return;
// Update priority list to calculate the last updated input
formCopy[formIndex].priorityList = formCopy[formIndex].priorityList.sort((a, b) => {
if (a === inputFieldName) return 1;
if (b === inputFieldName) return -1;
else return 0;
});
// Update selected input field
formCopy[formIndex].inputs = formCopy[formIndex].inputs.map((input) => {
if (input.name === inputFieldName) {
input.value = e.target.value;
}
return input;
});
// If more than two inputs empty do not calculate
const emptyInputs = formCopy[formIndex].inputs.reduce(
(acc, nV) => (nV.value === "" ? (acc += 1) : acc),
0
);
// Calculate the last edited input field
formCopy[formIndex].inputs = formCopy[formIndex].inputs.map((input) => {
if (input.name === formCopy[formIndex].priorityList[0] && emptyInputs <= 1) {
const calculatedValue = formCopy[formIndex].calculate(formCopy[formIndex].priorityList[0]);
input.value = calculatedValue;
}
return input;
});
// Final set hook, now with calculated value
setformsEQ([...formCopy]);
};
const formInputs = formsEQ.map((formEQ) => {
return (
<form className="form" key={formEQ.name}>
{formEQ.inputs?.map((formInput) => {
return (
<div className="form__input" key={formInput.name}>
<label>{formInput.label}: </label>
<input
name={`${formEQ.name}-${formInput.name}`}
onChange={inputsHandler}
placeholder={`${formInput.label} (${formInput.selectedUnit})`}
value={formInput.value}
/>
</div>
);
})}
</form>
);
});
return <div>{formInputs}</div>;
}
export default Calculator;
Upvotes: 0
Views: 2862
Reputation: 157
To whomever is reading this and is a rookie in react like me. The solution for me was to use useEffect hook; And whenever useSelector updates the state constant, the useEffect hook will use the useState set function to update the state.
See added code below that fixed my problem:
useEffect(() => {
setformsEQ([...state])
}, [state])
Upvotes: 3