This is for an interactive bible.
In the following code I have buttons that are being conditionally rendered based upon the buttons I previously clicked.
The code calls an API to retrieve the data.
useEffect(() => {
dispatch(fetchBibleBooks());
}, [dispatch]);
useEffect(() => {
if(selectedBook && chapter){
dispatch(fetchBibleBookChapter( {selectedBook, chapter} ));
}
}, [dispatch, selectedBook, chapter]);
useEffect(() => {
setOTBooks(bibleArray.filter(book => book.testament === 'OT'));
}, [ clickedOT ]);
useEffect(() => {
setNTBooks(bibleArray.filter(book => book.testament === 'NT'));
}, [ clickedNT ]);
useEffect(() => {
setBookChapters(bibleArray.find(book => book.id === selectedBook)?.chapters || []);
}, [ selectedBook ]);
useEffect(() => {
bibleArray.filter(chapter => chapter);
const subsetBibleArray = bibleArray.map( text => text.cleanText );
setChapterText(subsetBibleArray);
});
return(
<Container>
<Row>
<Col style={{textAlign: 'center'}}>
<h1 style={{ color: 'darkblue'}}>Biblia Interactiva</h1>
<Button style={{margin: '2rem'}} color='warning' size='lg' onClick={() => setClickedOT(true)}>
Antiguo Testamento
</Button>{' '}
<Button style={{margin: '2rem'}} color='warning' size='lg' onClick={() => setClickedNT(true)}>
Nuevo Testamento
</Button>
</Col>
</Row>
<Row style={{marginTop:'4rem'}}>
<Col style={{textAlign:'left', marginLeft:'-18rem'}}>
{
clickedOT && otBooks.sort((b1, b2) => {
if (b1.order < b2.order) return -1;
if (b1.order > b2.order) return 1;
return 0;
}).map(book => (
<Button color='primary' key={book.order} style={{ margin: '4px' }} onClick={() => setSelectedBook(book.id)}>
{book.name}
</Button>
))
}
</Col>
<Col style={{textAlign:'left', marginRight:'-18rem'}}>
{
clickedNT && ntBooks.sort((b1, b2) => {
if (b1.order < b2.order) return -1;
if (b1.order > b2.order) return 1;
return 0;
}).map(book => (
<Button color='primary' key={book.order} style={{ margin: '4px' }} onClick={() => setSelectedBook(book.id)}>
{book.name}
</Button>
))}
</Col>
</Row>
<Row>
<Col style={{textAlign:'center', marginLeft:'', marginTop: '6rem'}}>
{
bookChapters.map(ch => (
<Button color='success' key={ch.id} style={{ margin: '4px' }} onClick={() => setChapter(ch.chapter)}>
{ch.chapter}
</Button>
)
)
}
</Col>
</Row>
<Row>
<Col style={{textAlign:'left', marginLeft:'', marginTop: '6rem'}}>
{
chapterText.map((text, idx) => (
<p key={idx}>
{text}
</p>
)
)
}
</Col>
</Row>
</Container>
)
}
The code work well the first go around but if I re-click one of the Bible book buttons, the previously rendered chapter buttons will disappear and so will the text, which is fine but it should re-render the chapter buttons to the new relevant book clicked.
The 'selectedBook' variable is updated but the state variable 'chapter' is still holding the value of the previous chapter and so the new state has the new book with the old chapter and fetches that chapter from the API and the relevant chapter buttons don't get rendered.
You don’t need Effects to handle user events. In short - don't do things in effects (which might "work" but makes it incredibly hard to read for anybody but the author), just do them directly in the event handlers. While I'm sure your code could be "fixed" in it's current form, it'd be a great exercise to re-write it to clearly understand the data flow.