React functional components async props to children - reactjs

I have a functional component (App.js) where I want to fetch some initial data using useEffect.
useEffect(() => {
const init = async () => {
const posts = await getPosts(0, 3);
const newArticles = await getArticles(posts);
setArticles(() => [...articles, ...newArticles]);
};
init();
}, []);
then I want to pass the result to a child
<ArticleList articles={articles}></ArticleList>
but in the Article component I get an empty array when I try to console.log the props.
useEffect(() => {
console.log(props.articles);
setArticles(() => props.articles);
}, [props.articles]);
How can I solve this issue?

Related

undefined children on react parent. However when console.log state is ldefined

I'm trying to load a component using useEffect hook and axios. However, when loading the page the components are not render, but when inspecting the parent component, his children are undefined (I'm very new to react)
Component
export const BonusRows = () => {
const [state, setState] = useState([]);
const [order, setOrder] = useState("asc");
const bonusRequest = () => {
axios
.get(`http://localhost:4000/api/v1/process/creator/gutierad5`)
.then((res) => {
const bonus = res.data;
const processes = bonus.processes;
setState(processes);
console.log(processes);
});
};
useEffect(() => {
bonusRequest();
}, []);
return (
<ProcessTable
funcionality={() => {
sortByDate(state, setState, order, setOrder);
}}
>
<Accordion allowToggle allowMultiple>
{state.map((element, index) => {
<AccordionItemSlot
key={index}
proccessID={element.id}
title={element.name}
targeruser='gutierad5'
createDate={FormatDateInYYMMDD(element.createdAt)}
status={element.status}
creator={element.creator}
links={element.links}
amount={element.processBonus.amount}
updatedAt={element.updatedAt}
password={element.processBonus.password}
franchise={element.processBonus.franchise}
/>;
})}
</Accordion>
</ProcessTable>
);
};
I don't know what I'm doing wrong. My best guess is that the state is not present when the component is loaded, so thet's the reasong is undefined. However when console log the state, the data is there.

React Redux - useState Hook not working as expected

I have 2 actions in redux (both async) and I'm calling them both within my functional component via dispatch; the first using useEffect and the second via a button click. What I want to do is dispatch the actions to retrieve them from an async function, then use them within my component via useState. But using the useState is not rendering.
Here is my component:
export default function Hello()
{
const { first, second } = useSelector(state => state.myReducer);
const dispatch = useDispatch();
const fetchFirst = async () => dispatch(getFirst());
const fetchSecond = async () => dispatch(getSecond());
const fetchFixturesForDate = (date: Date) => dispatch(getFixturesForDate(date));
const [superValue, setSuperValue] = useState('value not set');
useEffect(() => {
const fetch = async () => {
fetchFirst();
setSuperValue(first);
};
fetch();
}, []);
const getSecondOnClickHandler = async () =>
{
console.log('a')
await fetchSecond();
setSuperValue(second);
}
return (
<div>
<p>The super value should first display the value "first item" once retrieved, then display "second value" once you click the button and the value is retrieved</p>
<p>Super Value: {superValue}</p>
<p>First Value: {first}</p>
<p>Second Value: {second}</p>
<button onClick={async () => await getSecondOnClickHandler()}>Get Second</button>
</div>
)
}
The superValue never renders even though I am setting it, although the value from first and second is retrieved and displayed.
StackBlitz.
Any help?
The value of first and second inside your two useEffects is set when the component mounts (I guess at that point they are undefined). So in both cases you will be setting superValue to that initial value.
You have two options:
Return the first/second values back from fetchFirst and fetchSecond, so that you can retrieve them directly from the executed function, and then set superValue:
useEffect(() => {
const fetch = async () => {
const newFirst = await fetchFirst();
setSuperValue(newFirst);
};
fetch();
}, []);
Add separate useEffects that listen for changes to first and second
useEffect(() => {
setSuperValue(first)
},[first])
useEffect(() => {
setSuperValue(second)
},[second])
The value in the reducer is not necessarily set when the action is dispatched, e.g. after fetchFirst() is called. Also the await that you do in await fetchSecond();
doesn't help since the reducer function is not executed.
You could add useEffect hooks and remove the setSuperValue from the other methods, but I think the code gets quite complicated.
What problem are you trying to solve in the first place?
useEffect(() => setSuperValue(first), [first]);
useEffect(() => setSuperValue(second), [second]);
useEffect(() => {
const fetch = async () => {
fetchFirst();
};
fetch();
}, []);
const getSecondOnClickHandler = async () => {
console.log('a');
await fetchSecond();
};
https://stackblitz.com/edit/react-ts-hsqd3x?file=Hello.tsx

React functional component re-renders with old data

I have this functional component that is used as a child component. Like this:
//... in the ParentComponent
<ChildComponent
id={somevalue}
/>
and the Child Component looks like this:
const ChildComponent = ({
id
}) => {
const [rawData, setRawData] = useState([]);
const [processedData, setProcessedData] = useState([]);
useEffect(() => {
// ... do some time consuming calculations
SetProcessedData(data);
}, [rawData]);
useEffect(() => {
console.log('useEffect');
// fetch data with axios and ...
() => {
const reloadData = async () => {
axios.request({
url,
data,
...config,
})
.then((result) => {
SetRawData(data);
})
};
reloadData();
}, [id]);
console.log('Render');
return(
{processedData.map( ....) }
);
}
The first time the ChildComponent is render everything works like charm.
The problem occurs when somevalue and the prop (id) is changed.
The child sees the new props and rerenders the component AND calls useEffect[id]
The problem is that the render happens BEFORE the useEffect so the processedData state is still old.
I tried to avoid the first rerender by setting a isLoading variable but the first place I can do it is the useEffect[id] which is called AFTER the rerender.
So the console shows:
Render
useEffect
What am I missing?

Array of hooks in React

I'm starting to use hooks in React and I got stuck, when I realized I would need an array of hooks to solve my problem. But according to the Rules of Hooks
Only Call Hooks at the Top Level
I'm not allow to call hooks inside a loop (and I guess also not in map).
My custom hook subscribes to an API and adds data to the state when there is an update:
export const useTrace = (id) => {
[trace, setTrace] = useState([])
useEffect(() => {
Api.getCurrentTrace(id)
.then(currentTrace => {
setTrace(currentTrace)
})
}, [id])
useEffect(() => {
Api.subscribeTraceUpdate(onUpdateTrip)
return () => {
Api.unsubscribeTraceUpdate(onUpdateTrip)
}
}, [])
const onUpdateTrip = msg => {
if (msg.id === id) {
setTrace([msg.data].concat(trace))
}
}
}
In my component I have a state with an array of IDs. For each ID I would like to use the useTrace(id) hook somehow like this:
import DeckGL from '#deck.gl/react'
function TraceMap({ ids }) {
const data = ids.map((id) => ({
id,
path: useTrace(id)
}))
const pathLayer = new PathLayer({
id: 'path-layer',
data,
getPath: d => d.path
})
return <DeckGL
layers={[ pathLayer ]}
/>
}
For the sake of simplicity I got ids as a property instead of having a state.
Why not have a useTraces custom hook rather than useTrace. This new hook can take an array of ids instead of a single id.
export const useTraces = (ids) => {
[traces, setTraces] = useState([]);
useEffect(() => {
(async () => {
const traces = await Promise.all(
ids.map((id) => Api.getCurrentTrace(id))
);
setTraces(traces);
})();
}, [ids]);
// ...
};

Class Component to Functional component

How do I convert this class component to a functional component?
What I am trying to achieve is to subscribe and unsubscribe from firebase using useEffect()
class PostsProvider extends Component {
state = { posts: [] }
unsubscribeFromFirestore = null;
componentDidMount = () => {
this.unsubscribeFromFirestore = firestore
.collection('posts')
.onSnapshot(snapshot => {
const posts = snapshot.docs.map(collectIdAndDocs);
this.setState({ posts });
});
}
componentWillUnmount = () => {
this.unsubscribeFromFirestore();
}
This is how I'd convert your component. You'd useState() to create your posts state and then a useEffect is pretty straightforward to move. The main thing you'd want to make sure of is that your dependency array is correct for it so it doesn't subscribe and unsubscribe too often (or not often enough).
function PostsProvider(){
const [posts,setPosts] = useState([]);
useEffect(() => {
const unsubscribeFromFirestore = firestore
.collection('posts')
.onSnapshot(snapshot => {
const posts = snapshot.docs.map(collectIdAndDocs);
setPosts(posts);
});
return () => {
unsubscribeFromFirestore();
}
}, [])
}

Resources