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.
Two things:
and then have all your buttons call this function
bookChaptersis computed/derived as a consequence of a book being selected (or not), but it's not state itself.That's it. It gets recomputed on every render and that's fine (unless you have a performance issue). You don't need state to to hold
bookChapters.It's unlikely that
chapterTextis state as well, it shouldn't be state and the effect shouldn't exist.See this You might not need an effect