I am trying to display calculated values from a function called renderAnalysis - all which works until the bottom of the function where I am trying to return a div with the values that are found. However, this isn't displaying (all values are returned though in the console when I test it out) and was wondering what I need to adjust to prevent the values I can show in the console? I think I'm doing a render call wrong or something... I've added the code for reference. Thank you!
renderAnalysis = () => {
let reactions = this.state.reactions;
let foods = Object.keys(reactions);
foods.map((food) => {
let reactionDescriptions = reactions[food];
let reactionDescriptionKeys = Object.keys(reactionDescriptions);
let responseKey = Object.keys(reactionDescriptions);
responseKey.map((singleDescription) => {
let value = reactionDescriptions[singleDescription];
// RETURN VALUE NOT SHOWING
return <div> {(food, singleDescription, value)} </div>;
});
});
};
Further information: There is a toggle I made in state that if someone clicks a button it triggers this function. Basically
<button onClick={this.displayRenderAnalysisToggle}>
Render Analysis
</button>{" "}
<br />
{this.state.displayRenderAnalysisToggle ? this.renderAnalysis() : null}
</div>
In my react app I need to return a line which will be created based on a list.
Here is the object,
searchCriteria: {
op_company: "039",
doc_type: "ALL"
}
and in my UI, i need to show it as a paragraph with bold values. So the hard coded code would be like below
<p>Download request for op_company: <b>{searchCriteria.op_company}</b>, doc_type: <b>{searchCriteria.doc_type}</b></p>
But the object(searchCriteria) will be changed based on the user request. So I tried like below.
const getSearchCriteria = (criteria) => {
let searchCriteria = []
searchCriteria.push('Download request for')
Object.keys(criteria).forEach((key) => {
if(criteria[key] !== '') {
searchCriteria.push(` ${key}: ${criteria[key]},`)
}
});
return searchCriteria;
}
return (
<p>
{getSearchCriteria(searchCriteria).map((item) => <span key = {item}>{item}</span>)}
</p>
);
here i'm getting the expected output. But I can't get the value as bold (highlighted). Is there another way to directly deal with html elements?
I have a Next and Prev button below a list on my spfx based webpart.
I also have another list below it with another two Next and Prev buttons.
I want to distinguish between the two sets of buttons and use the two functions that I'm using already:
private _listANext(e) {
let currentPage = this.state.ListAPage;
let highestPageNo = this.state.ListAPages;
currentPage = Math.min(highestPageNo,currentPage + 1 );
this.setState({
ListAPage: currentPage,
});
console.log(e.target.id);
}
private _listAPrev(e) {
let currentPage = this.state.ListAPage;
currentPage = currentPage <= 1 ? 1 : currentPage - 1;
this.setState({
ListAPage: currentPage,
});
console.log(e.target.id);
}
and the buttons in the render:
<DefaultButton
id="aprev"
text="Prev"
onClick={(e) => this._listAPrev(e)}
/>
<span> {' '}</span>
<DefaultButton
id="anext"
text="Next"
onClick={(e) => this._listANext(e)}
/>
The two functions give me the id of the buttons but if I were to use the id's within an if, would the id's stay the same on different browsers? I think there is a better way to do what I'm trying to do!
<DefaultButton
id="aprev"
text="Prev"
onClick={(e) => this._listAPrev(e,id)}
/>
you can pass the id by this.and get the id and use it.
private _listAPrev(e,id) {}
I just started to learn ReactJS and encountered a problem that I can't solve.
I'm creating a basic application for TV Shows. I have a bootstrap tab for every season of a show and within this tab I want to list all the episodes of the selected season. The problem is that I have to create the tabs in a loop, and within this loop I should have another loop for the episodes.
I'm trying to do something like this:
EpisodeCards(props) {
return (
<div>
This should contain the details of the episodes
</div>
)
}
SeasonTabs(props) {
console.log('Seasons: ', props.seasons)
let tabs = [];
let episodes = [];
let currentSeason = 1;
let id;
let aria;
for(let season of props.seasons) {
episodes = [];
id="nav-season" + currentSeason;
aria = "nav-season" + currentSeason + "-tab";
tabs.push(<div className="tab-pane fade" id={id} role="tabpanel" aria-labelledby={aria}><this.EpisodeCards episodes={season}></this.EpisodeCards></div>);
currentSeason++;
}
return (
<div className="tab-content py-3 px-3 px-sm-0" id="nav-tabContent">
{tabs}
</div>
)
}
For this I am getting the following error:
Unhandled Rejection (TypeError): Cannot read property 'EpisodeCards' of undefined
How can this be done in the 'react way'? Thanks in advance.
Change
SeasonTabs(props)
to
SeasonTabs = (props) =>
You want to access a class property using this but by default its not binded to the function (ES5 only) by creating the functions using arrow () =>(new ES6 syntax) it automatically bind this to the function.
For Example:
class Test extends Component{
constructor(props){
this.testFn= this.testFn.bind(this);
}
testFn(){
console.log(this); //will output
}
testFn2(){
console.log(this); // undefined
}
testFn3 = () =>{
console.log(this); //will output
}
}
Reactjs is all about components, everything is component. If a function return a react element or Custom element then is a component.
You are creating a functional component inside of a class component, while this approach may work but this is not appropriate way to make component.
It is better to use composition, your code will be more clear, easier
to read, better maintainability and you can use component in other
components.
My solution:
function EpisodeCards(props) {
return (
<div>
This should contain the details of the episodes
</div>
)
}
SeasonTabs(props) {
console.log('Seasons: ', props.seasons)
let tabs = [];
let episodes = [];
let currentSeason = 1;
let id;
let aria;
for(let season of props.seasons) {
episodes = [];
id="nav-season" + currentSeason;
aria = "nav-season" + currentSeason + "-tab";
//There is no need for this keyword
tabs.push(<div className="tab-pane fade" id={id} role="tabpanel" aria-labelledby={aria}><EpisodeCards episodes={season}/></div>);
currentSeason++;
}
return (
<div className="tab-content py-3 px-3 px-sm-0" id="nav-tabContent">
{tabs}
</div>
)
}
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