I'm sorry if the answer is obvious. I probably missed a pattern to do that...
I have this kind of architecture (more complexe, of course) : A component (here a button) have to call a function and to set a state in the same time.
Of course, This example don't work because the state to displayed word
isn't defined before the speak()
function is called.
What is the best way to do this ?
const Speaker = () => {
const [word, setWord]= useState ("nothing")
const speak = () => {
console.log(word)
...
}
const speakDirectly = () => {
setWord("hello")
speak() // say "nothing" instead of "hello" 😞
}
const prepareWord = (wordToPrepare) => {
setWord(wordToPrepare)
}
return (
<>
<p>Thinking word : {word} </p>
<button onClick={() =>prepareWord('Goodbye')} >Prepare to say "Goodbye"</button>
<button onClick={() =>prepareWord('Goodnight')} >Prepare to say "Goodnight"</button>
----
<button onClick={() =>speak()} >Say Hello</button>
<button onClick={() =>speakDirectly('hello')} >Say Hello</button>
</>
)
}
Does exists a pattern to resolve this behaviour in React?
I want to avoid to update my functions like this 🤢
const Speaker = () => {
const [word, setWord]= useState ("nothing")
const speak = (directWord) => {
let wordToSay = directWord || word
console.log(word)
...
}
const speakDirectly = (directWord) => {
setWord(directWord)
speak(directWord)
}
...
You could do something like this. Have a state for your word and your direct word. Use an effect for your direct word to trigger speak whenever it's updated, but call speak on click for your prepared word.
const Speaker = () => {
const [preparedWord, setPreparedWord]= useState();
const [directWord, setDirectWord]= useState();
const speak = (word) => {
console.log(word)
...
}
useEffect(() => directWord && speak(directWord), [directWord]);
return (
<>
<p>Thinking word : {preparedWord} </p>
<button onClick={() => setPreparedWord('Goodbye')} >Prepare to say "Goodbye"</button>
<button onClick={() => setPreparedWord('Goodnight')} >Prepare to say "Goodnight"</button>
----
<button onClick={() => speak(preparedWord)} >Speak</button>
<button onClick={() => setDirectWord('hello')} >Say Hello</button>
</>
)
}
Update
After thinking about it for a second, unless you need the direct word to be in your state, you can get rid of that part too and just call speak on it while passing it to the function.
const Speaker = () => {
const [preparedWord, setPreparedWord]= useState();
const speak = (word) => {
console.log(word)
...
}
return (
<>
<p>Thinking word : {preparedWord} </p>
<button onClick={() => setPreparedWord('Goodbye')} >Prepare to say "Goodbye"</button>
<button onClick={() => setPreparedWord('Goodnight')} >Prepare to say "Goodnight"</button>
----
<button onClick={() => speak(preparedWord)} >Speak</button>
<button onClick={() => speak('hello')} >Say Hello</button>
</>
)
}