I have a state variable in my React Native Project. It needs to be updated when a specific function is called. However, the setState
method being asyncronous results in my variable being updated after it should.
What I need to happen is:
What is happening is:
setState
;To illustratehow this is being managed, this is what my code looks like:
const MyComponent: FC<MyComponentProps> = ({/*my component props*/}) => {
const [rows, setRows] = useState<Array<boolean>>([]);
useEffect(() => {
console.log('rows on useEffect: ', rows);
}, [rows]);
async function OnFocus(): Promise<void> {
await GetApplication();
}
async function GetApplication(): Promise<void> {
const _state: ApplicationParameters = await ctrl.getApplicationParameters(
ctrl.ctrl.getDevice()
);
console.log('_state.rows: ', _state.rows);
console.log('rows before setRows()', rows);
setRows(_state.rows!);
console.log('rows after setRows()', rows);
}
return(
<RowBars theme={theme} rows={rows} />
)
}
The log for the above code shows:
LOG _state.rows: [true, true, true, true, true, true, true, true, true, true, true]
LOG rows before setRows() []
LOG rows after setRows() []
LOG rows on useEffect() [true, true, true, true, true, true, true, true, true, true, true]
LOG rows on useEffect() [true, true, true, true, true, true, true, true, true, true, true]
If something triggers the state to update later, the array appears filled, but nothing is built.
The component where my variable is later used is as follows:
interface RowBarsProps {
theme: AppColors;
rows: Array<boolean>;
}
export const RowBars: FC<RowBarsProps> = ({ theme, rows }: RowBarsProps) => {
const [Rows] = useState<number>(rows.length);
const _rows = [];
for (let i = 0; i < Rows; i++) {
_rows.push(<RowBar theme={theme} isOn={rows[i]} />);
}
return <RowToRowLiteBars>{_rows}</RowToRowLiteBars>;
};
I tried:
rows
on useEffect
. However, I don't have a reliable variable to watch on the dependency array of useEffect
;rows
is updated the props variable hasn't been yet updated, it tries to set rows
from an undefined value.Looking closely on my code I found out that the problem was on the child component. What was happening was that it wasn't built for perceiving the changes in the parent component.
What I did was to create a useEffect()
to watch for changes in the coming rows
.
Don't know if it is the best practice, but it worked pretty well.
export const RowBars: FC<RowBarsProps> = ({ theme, rows }: RowBarsProps) => {
const [rowBars, setRowBars] = useState<RowBarsState>([]);
// Updates the row bars according to `rows` current state
useEffect(() => {
setRowBars(() => {
const newRowBars = rows.map((row) => <RowBar theme={theme} isOn={row} />);
return newRowBars;
});
}, [rows, theme]);
return <RowToRowLiteBars>{rowBars}</RowToRowLiteBars>;
};