Permalänk

React "conditional rendering" ?

Jag har precis börjat att försöka lära mig lite React. Jag har gjort en liten test-applikation som har ett antal olika knappar som är kopplade till useStates för att hålla ordning på hur många gånger en enskild knapp har blivit klickad.
Nu har jag tre fält under knapparna som visar antalet klick för varje knapp. Men jag skulle vilja ha ytterligare lite statistik som även tar med procent för de olika knapparna.

Den statistiken kan jag ju inte visa innan jag har klickat på någon knapp första gången. Hur gör jag för att innan jag klickat något visa en <p>Ingen statistik tillgänglig</p> och sen efter att jag klickat en gång på någon av knapparna så ska den paragrafen försvinna och ersättas av själva statistiken?

Jag försökte först med if-satser i varje statestik-komponent så att den enskilda statestiken bara returneras om count > 0. Men det gick inte då jag antar att jag alltid måste returnera ett element. Men jag vill ju inte heller ha en massa tomma element vid den första renderingen.

Permalänk
Medlem

I din render, {this.state.variabelinnehållandestatistik ? rendera statistik : <p>ingen statistik tillgänglig</p>}

Visa signatur

Still play with birds like I hang with mother goose.

Permalänk

Jag har stött på ytterligare ett problem. Någon som har något tips på hur jag löser "hasRating" som jag använder överallt lite smidigare?

import React, { useState } from 'react' import ReactDOM from 'react-dom' const Button = ({ onClick, text }) => { return ( <> <button onClick={onClick}> {text} </button> </> ) } const Display = ({ text }) => { return ( <h1>{text}</h1> ) } const History = ({ hasRating }) => { if (!hasRating) { return ( <> <h1>statistics</h1> <p>No feedback given</p> </> ) } return (<h1>statistics</h1>) } const Statistics = ({ text, value, hasRating }) => { if (hasRating) { return ( <tr> <td>{text}</td> <td>{value}</td> </tr> ) } return null } const average = (good, bad, total) => { return ((good - bad) / total) } const positive = (good, total) => { return ((good / total) * 100 + " %") } const App = () => { // save clicks of each button to own state const [good, setGood] = useState(0) const [neutral, setNeutral] = useState(0) const [bad, setBad] = useState(0) const totalRatings = good + neutral + bad const hasRatings = totalRatings > 0 return ( <div> <Display text="give feedback" /> <Button onClick={() => setGood(good + 1)} text="good" /> <Button onClick={() => setNeutral(neutral + 1)} text="neutral" /> <Button onClick={() => setBad(bad + 1)} text="bad" /> <History hasRating={hasRatings} /> <table> <tbody> <Statistics text="good" value={good} hasRating={hasRatings} /> <Statistics text="neutral" value={neutral} hasRating={hasRatings} /> <Statistics text="bad" value={bad} hasRating={hasRatings} /> <Statistics text="all" value={totalRatings} hasRating={hasRatings} /> <Statistics text="average" value={average(good, bad, totalRatings)} hasRating={hasRatings} /> <Statistics text="positive" value={positive(good, totalRatings)} hasRating={hasRatings} /> </tbody> </table> </div> ) } ReactDOM.render(<App />, document.getElementById('root') )

Permalänk

@burton666: Du vill inte att din komponent Statistics ska bry sig om huruvida hasRating är sann eller inte. Den bryr sig bara om hur den ska rita ut "Statistics" så att skicka ner hasRating känns fel. Samma med History.

Permalänk

@EnsamRobot4: Jo jag fick den känslan jag med men vet inte riktigt hur jag ska lösa det.

Permalänk
Medlem
Skrivet av burton666:

Jag har stött på ytterligare ett problem. Någon som har något tips på hur jag löser "hasRating" som jag använder överallt lite smidigare?

import React, { useState } from 'react' import ReactDOM from 'react-dom' const Button = ({ onClick, text }) => { return ( <> <button onClick={onClick}> {text} </button> </> ) } const Display = ({ text }) => { return ( <h1>{text}</h1> ) } const History = ({ hasRating }) => { if (!hasRating) { return ( <> <h1>statistics</h1> <p>No feedback given</p> </> ) } return (<h1>statistics</h1>) } const Statistics = ({ text, value, hasRating }) => { if (hasRating) { return ( <tr> <td>{text}</td> <td>{value}</td> </tr> ) } return null } const average = (good, bad, total) => { return ((good - bad) / total) } const positive = (good, total) => { return ((good / total) * 100 + " %") } const App = () => { // save clicks of each button to own state const [good, setGood] = useState(0) const [neutral, setNeutral] = useState(0) const [bad, setBad] = useState(0) const totalRatings = good + neutral + bad const hasRatings = totalRatings > 0 return ( <div> <Display text="give feedback" /> <Button onClick={() => setGood(good + 1)} text="good" /> <Button onClick={() => setNeutral(neutral + 1)} text="neutral" /> <Button onClick={() => setBad(bad + 1)} text="bad" /> <History hasRating={hasRatings} /> <table> <tbody> <Statistics text="good" value={good} hasRating={hasRatings} /> <Statistics text="neutral" value={neutral} hasRating={hasRatings} /> <Statistics text="bad" value={bad} hasRating={hasRatings} /> <Statistics text="all" value={totalRatings} hasRating={hasRatings} /> <Statistics text="average" value={average(good, bad, totalRatings)} hasRating={hasRatings} /> <Statistics text="positive" value={positive(good, totalRatings)} hasRating={hasRatings} /> </tbody> </table> </div> ) } ReactDOM.render(<App />, document.getElementById('root') )

Nu har jag aldrig använt React hooks, så det var intressant att se hur man kan göra det utan class components. Dock känns det lite rörigare att förstå, men är säkert nice då man jobbat med det, globalt state likt Redux.

Var även ett tag sen jag satt med React, så ska se om jag kan lyckas hjälpa dig!

Som ovan nämner så behöver inte Statistics veta om ifall ett objekt har en rating, det ända den vill göra är att printa ut något till DOM, samma gäller History.

Så i din render skulle vi kunna göra exempelvis så här:

<h1>statistics</h1> { !hasRatings && <p>No feedback given</p> } <table> <tbody> { hasRatings && <Fragment> <Statistics text="good" value={good} /> <Statistics text="neutral" value={neutral} /> <Statistics text="bad" value={bad} /> <Statistics text="all" value={totalRatings} /> <Statistics text="average" value={average(good, bad, totalRatings)} /> <Statistics text="positive" value={positive(good, totalRatings)} /> </Fragment> } </tbody> </table>

Jag har valt att ta bort History då jag tycker den funktionen inte har något riktigt syfte. Du kan lika gärna sätta en <h1> i din render här. Sen med en check på hasRatings om den är false så kan du visa No feedback given.

Sedan kan du med hjälp av React.Fragmemt gruppera flera nodes.
Här säger vi att OM hasRatings är true OCH skriv ut detta (vilket alltid är true).

Plockar bort hasRatings från Statistics

const Statistics = ({ text, value }) => { return ( <tr> <td>{text}</td> <td>{value}</td> </tr> ) }

TIPS:
Du kan se över om du kan hålla dina värden i en array, så att du kan få ut alla dina värden via en .map(). På så vis slipper du skriva flera olika <Statistics>, då functionen ändå är dynamisk och kan hantera alla värden du skickar in.
Sparar tangenttryck för trötta fingrar och gör koden lite kortare och lättare att jobba med om man skulle vilja utöka.

Exempel

state = { ratings: [ good = { text: "textvalue", value: 1 }, bad = { text: "textvalue", value: 2 }, ...etc ] } this.state.ratings.map((rating, i) => { return (<Statistics text={rating.text} value={rating.value} key={i} />) });

Permalänk

Tackar för ett bra svar. Nu har jag ett nytt problem. Hur får jag "handleMaxVote" att returnera en sträng med ett line break? Om jag istället returnerar ett array med ett <br/> element i mitten så funkar det som jag vill men jag får ett felmeddelande i developer-console i chrome ("VM7053:37 Warning: Each child in a list should have a unique "key" prop.").

import React, { useState } from 'react' import ReactDOM from 'react-dom' const Random = () => Math.floor(Math.random() * anecdotes.length) const Button = ({ text, onClick }) => { return ( <button onClick={onClick}>{text}</button> ) } const Display = ({text}) => { return(<p>{text}</p>) } const handleVote = (selected, vote, setVote) => { const tempArray = [...vote] tempArray[selected]++ setVote(tempArray) } const handleMaxVote = (anecdotes, vote) => { const maxValue = Math.max(...vote) const maxValueIndex = vote.indexOf(maxValue) const maxVotedAnecdote = anecdotes[maxValueIndex] return [maxVotedAnecdote, <br />, "hello"] } const App = (props) => { const [selected, setSelected] = useState(Random()) const [vote, setVote] = useState(new Array(6).fill(0)) return ( <div> <h1>Anecdote of the day</h1> <Display text={anecdotes[selected]} /> <Display text={'has ' + vote[selected] + ' votes'} /> <Button text="vote" onClick={() => handleVote(selected, vote, setVote)}/> <Button text="next anecdote" onClick={() => setSelected(Random())} /> <h1>Anecdote with most votes</h1> <Display text={handleMaxVote(anecdotes, vote)} /> </div> ) } const anecdotes = [ 'If it hurts, do it more often', 'Adding manpower to a late software project makes it later!', 'The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.', 'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.', 'Premature optimization is the root of all evil.', 'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.' ] ReactDOM.render( <App anecdotes={anecdotes} />, document.getElementById('root') )

Permalänk
Medlem
Skrivet av burton666:

Tackar för ett bra svar. Nu har jag ett nytt problem. Hur får jag "handleMaxVote" att returnera en sträng med ett line break? Om jag istället returnerar ett array med ett <br/> element i mitten så funkar det som jag vill men jag får ett felmeddelande i developer-console i chrome ("VM7053:37 Warning: Each child in a list should have a unique "key" prop.").

const handleMaxVote = (anecdotes, vote) => { const maxValue = Math.max(...vote) const maxValueIndex = vote.indexOf(maxValue) const maxVotedAnecdote = anecdotes[maxValueIndex] return (maxVotedAnecdote + "\nhas " + maxValue + " votes") } const App = (props) => { const [selected, setSelected] = useState(Random()) const [vote, setVote] = useState(new Array(6).fill(0)) return ( <div> <h1>Anecdote of the day</h1> <Display text={anecdotes[selected]} /> <Display text={'has ' + vote[selected] + ' votes'} /> <Button text="vote" onClick={() => handleVote(selected, vote, setVote)}/> <Button text="next anecdote" onClick={() => setSelected(Random())} /> <h1>Anecdote with most votes</h1> <Display text={handleMaxVote(anecdotes, vote)} /> </div> ) } const anecdotes = [ 'If it hurts, do it more often', 'Adding manpower to a late software project makes it later!', 'The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.', 'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.', 'Premature optimization is the root of all evil.', 'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.' ]

Jag kan svara för att just key är en del av React, React använder detta för att nagivera i trädet och minimera uppdatering. Varje key ska vara unik, detta är för att minimera re-renders.
I vanliga fall använder man ett ID från en datakälla eller generarer det via någon helper class, detta är oftast i typen GUID.
Men i enklare fall går det att använda loop-index, även om det inte är så jätte bra.
Kan ju finnas flera keys med value 1, 2, 3 osv och plockar du bort eller lägger till element i din array så blir dessa IDn fel.

Detta är dock inget som gör att ditt program inte fungerar, det handlar snarare om best-practise och prestanda.

Mer info här: stackoverflow

Angående ditt fel så verkar det saknas kod, jag kan inte skapa upp en fiddle och testa, vilket gör det svårt att felsöka bara sådär i forumet

Permalänk

@zaibuf: Jag har uppdatera koden nu och tagit med allt

Permalänk
Medlem
Skrivet av burton666:

@zaibuf: Jag har uppdatera koden nu och tagit med allt

Har lite funderingar angående koden.

Du passar in arrayen i props till App.
Du hänvisar direkt till din array "ancedotes" på ett flertal ställen, vad använder du props till?

I din handleMaxVote passar du in två olika arrayer, en för dina textvärden och en som jag antar håller i rösterna?

Enklast hade varit om du använt en array av object där varje ancedote har sitt textvärde och sin vote.
Då skulle du kunna köra en reduce på arrayen, där du plockar ut den med högst värde på vote.
Exempel fiddle

För att få bort ditt error kan du göra såhär:

const handleMaxVote = (anecdotes, vote) => { const maxValue = Math.max(...vote); const maxValueIndex = vote.indexOf(maxValue); const maxVotedAnecdote = anecdotes[maxValueIndex]; return ( <span> {maxVotedAnecdote} <br /> </span> ); };

Problemet var att din Display renderar ett <p> tag och sen passar du in en hel array med 3 värden. Då gnäller den för den spottar ut en lista men du har inte satt några unika keys.