I'm struggling with conditional rendering views.
I usually use only double conditional rendering views, with a useState(true/false).
But today I need a triple conditional rendering view.
How can I build something like if color = red, you render red, if color = blue you render blue, if color = green you render green.
const TripleConditionalRendering = () => {
const [ color, setColor ] = useState("")
const handleClickRed = () => {
setColor("red")
}
const handleClickBlue = () => {
setColor("blue")
}
const handleClickRed = () => {
setColor("blue")
}
return (
<button onClick={handleClickRed}/>
<button onClick={handleClickBlue}/>
<button onClick={handleClickGreen}/>
{ color == red ? (
<p> the color is : red</p>
) : (
<p> the color is : blue or green, sorry cant make triple conditional rendering</p>
)}
// how can I add those ?
<p> the color is : blue</p>
<p> the color is : green</p>
)
}
It looks like you've already worked out that if it's not red, it must be blue or green.
Nesting logic
The format for a ternary is as follows:
predicate ? true_expression : false_expression
Since you already know that if the statement is false, it can be one of the other options, just nest the ternary, like so:
predicate ? true_expression : (nested_predicate ? nested_true : nested false)
In your specific example, this becomes:
color == red
? <red/>
: color == blue
? <blue/>
: <green/>
Of course, logically, there's no reason why the logic for blue and green should be nested under the logic for red, or why any color should be nested under another. In this case it might make more sense and be more readable if there was one ternary per color:
{ color == red ? <red/> : null }
{ color == blue ? <blue/> : null }
{ color == green ? <green/> : null }
Or perhaps a switch statement would be even more readable
Related
I have such an div element:
<div className="rate" >
<mark className="rate-mark"> Rate : </mark>
<mark className="val-mark" style={{color}}> {rate}</mark>
</div>
where the color is a hook:
var green = "#0cb87f";
var red = "#ce2020";
const [color,setColor] = useState(green)
I'm trying to dynamically change the color like this :
if (response.getRate() < rate){
setColor(red)
}else{
setColor(green)
}
But in my case it turns out that the light is always green.I think the mark page element is just never updated.How can this be made to work?
Is there any possiblity to display partial color to the string the devextreme data grid cell.
For example values which are greather than 5000 should be in red and rest should have default color.
Eg:
The devextreme datagrid has a cell with value "4995,4218,4445,4506,5145". I need to show only 5145 as red and rest values should not apply any color, because only 5145 is there 5000.
You can use a cell template to accomplish this.
<DataGrid>
<Column
dataField="myValues"
cellRender={renderGridCell}
/>
</DataGrid>
const renderGridCell = (cellData) => {
return (
// this is just an example. access the cellData parameter to get the values of your object
[4995,4218,4445,4506,5145]
.map(x => <span style={{ backgroundColor: x > 5000 ? 'red' : undefined }}>{x}</span>)
.reduce((acc, x) => acc === null ? x : <>{acc}, {x}</>, null)
);
}
Here is the cell data parameter description. You probably need to access cellData.value or cellData.data.
I have a scenario like showing text as well adding class togathered. it look like i require to add multiple times with same elements nearly. what would be the correct approach for this kind of scenarios?
here is my template:
<span><a className={this.state.showMore ? 'active' : ''} onClick={this.showMore}>{this.state.showMore ? 'read less' : 'read more'}</a></span>
i have added the state showMore both a tag and the text inside. is there any simple way to handle same conditions across page?
Thanks in advance.
I'd create a component to handle read-more, and pass the props from where it's used if there's any, So same functionality is same across my application and if there's any improvements I can handle by it in one single place.
here is a demo
EX: functional component
export const ReadMore = ({ text, truncateLength = 10 }) => {
const [showMore, setShowMore] = useState(false);
const getText = () => {
if (showMore) {
return text;
}
const truncatedText = text.substring(0, truncateLength);
if (text.length > truncateLength) {
return `${truncatedText}...`;
}
return truncatedText;
};
return (
<span>
{getText()}
<a
className={showMore ? "active" : ""}
onClick={() => setShowMore(!showMore)}
>
{text.length > truncateLength && (showMore ? "read less" : "read more")}
</a>
</span>
);
};
and use it like this props could be:
text: is the text that should be read-less or more.
truncateLength: is the length that should show if the text length is
greater, and optional prop, if this isn't provided ReadMore
component will set the value to 10 by default, (check the props of
ReadMore)
<ReadMore
text="this is the text that should do the react-more and read-less"
truncateLength={10}
/>
{this.state.showmore ?
<span><a className={'active'} onClick={this.showMore}>read less</a></span>
:
<span><a onClick={this.showMore}>read more</a></span>
}
should be a more readable and clearer way of doing this. Basically when you have >1 thing depending on the same condition, take the condition outside would be my way to go!
const row = this.state.estimateItemList[i];
if (row.revision > this.state.selectedEstimate.revision) {
return row.style.color = 'red'; // i wanna change row color here set the css
}
return row;
};
I want to change the row color when the condition row.revision > this.state.selectedEstimate.revision holds. How can I prevent the change of this.color? It is my first react project.
Without knowledge of your row model, it's hard to understand what you want exactly, but this is one way of doing what you ask (I went off of what was defined for row)
render() {
return (
<div>
{this.state.estimateItemList.map((row) => {
const style = {};
if(row.revision > this.state.selectedEstimate.revision){
style.color = 'red';
}
return (
<div style={style}>{row.revision}</div>
);
)}
</div>
Having a array of items in a react state is not advisable as in your case it is hard to interpret what the row model is doing and it is advisable to keep only the row
which satisfies the condition in a state like the following.`
if (row.revision > this.state.selectedEstimate.revision) {
this.setState({
row_revision:row.revision
});
}`
and apply the class during render for the current state of row revision.Hope it helps.
I am using the module react-simple-contenteditable to enable editing of a fill in the blank worksheet. The reason I must use a content editable element instead of an input element is because I want the text of the problem to wrap. For example, if a problem has one blank, it divides the text into three sections the part before the blank, the blank, and the part after. If I were to represent the outer two as separate divs (or input fields), then the text would not wrap like a paragraph. Instead, I must have a single contenteditable div that contains an input field for the blank and free text on either side.
The text is wrapping like I want it, but when I type text in the contenteditable field, the cursor jumps to the beginning. I don't understand why because I tried the example on the module's github site and it works perfectly, and although my implementation is a bit more complicated, it works essentially the same.
Here is my render function that uses <ContentEditable /> :
render() {
const textPieces =
<div className='new-form-text-pieces'>
{
this.props.problem.textPieces.map( (textPiece, idx) => {
if (textPiece.blank) {
return (
<div data-blank={true} className='blank' key={ textPiece.id } style={{display: 'inline'}}>
<input
placeholder="Answer blank"
className='new-form-answer-input'
value={ this.props.problem.textPieces[idx].text }
onChange={ (event) => this.props.handleTextPiecesInput(this.props.problemIdx, idx, event.target.value) }
/>
<button className='modify-blank remove-blank' onClick={ (event) => this.props.removeBlank(this.props.problemIdx, idx) }>-</button>
</div>
);
} else {
let text = this.props.problem.textPieces[idx].text;
const placeholder = idx === 0 ? 'Problem text' : '...continue text';
// text = text === '' ? placeholder : text;
if (text === '') {
text = <span style={{color:'gray'}}>{placeholder}</span>;
} else {
}
return (
this.props.isTextSplit ?
<TextPiece
key={ textPiece.id }
problemIdx={this.props.problemIdx}
textPieceIdx={idx}
dropBlank={this.props.dropBlank}
moveBlank={this.props.moveBlank}
>
<div style={{display: 'inline-block', }}>{text}</div>
</TextPiece>
: text
);
}
})
}
</div>;
return (
this.props.isTextSplit ? textPieces :
<ContentEditable
html={ReactDOMServer.renderToStaticMarkup(textPieces)}
className="my-class"
tagName="div"
onChange={ (event, value) => this.props.handleProblemChange(event, this.props.problemIdx, value) }
contentEditable='plaintext-only'
/>
);
}
Here is the onChange function:
handleProblemChange(event, problemIdx) {
const problems = cloneDeep(this.state.problems);
event.target.children[0].childNodes.forEach( (textPieceNode, idx) => {
if (textPieceNode.constructor === Text) {
problems[problemIdx].textPieces[idx].text = textPieceNode.wholeText;
} else {
problems[problemIdx].textPieces[idx].text = textPieceNode.childNodes[0].value;
}
});
this.setState({ problems });
}
And here is the state it refers to, just to make thing clear:
this.state = {
problems: [
{
id: shortid.generate(),
textPieces: [
{
text : "Three days was simply not a(n)",
blank : false,
id: shortid.generate(),
},
{
text : "acceptable",
blank : true,
id: shortid.generate(),
},
{
text : "amount of time to complete such a lot of work.",
blank : false,
id: shortid.generate(),
}
]
}
Thanks so much
Long story short, there is no easy way to do this. I have tried this myself and spent days trying. Basically you have to save the cursor position and reposition it yourself after the update. All of this can be achieved with window.getSelection()
https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection
But it can get really tricky depending on how much your content has changed.
I ended up using draftJS instead. Which is an abstraction over contenteditable div by facebook themselves.
https://draftjs.org/docs/overview.html#content
A bit longer to pick up but you will be able to do a lot more
I had a similar problem using VueJS.
Here is the component containing the contenteditable div :
<Text #update-content="updateContent" :current-item-content="item.html_content"/>
Here is the prop definition in Text.vue component :
const props = defineProps({
currentItemContent: {
type: String,
default: ''
}
})
Here is the contenteditable div in Text.vue component :
<div
id="text-editor"
ref="text_editor"
class="mt-3 h-full w-full break-words"
contenteditable="true"
#input="updateContent"
v-html="currentItemContent"
>
Here is the method triggered on #update-content event
const item = computed(() => { ... })
(...)
function updateContent(content) {
item.value.html_content = content
}
The problem here is injecting the item.html_content value as a props triggers a re-render of the contenteditable div.
Because it's mutating it's value in the updateContent method and as the computed (item) is beeing updated, so does the prop value, v-html detects the updated value and triggers a re-render.
To avoid this, i removed the v-html binding :
<div
id="text-editor"
ref="text_editor"
class="mt-3 h-full w-full break-words"
contenteditable="true"
#input="updateContent"
>
And initialized the value of the contenteditable div in a onMounted hook :
const text_editor = ref(null)
(...)
onMounted(() => {
if (props.currentItemContent !== '') {
text_editor.value.innerHTML = props.currentItemContent
}
})
I don't know if there is a better solution for this but it's working fine for me. Hope this helps someone