React, dynamically add text to ref span - reactjs

I'm trying to render a message to a span tag specific to an item in a list. I've read a lot about React 'refs', but can't figure out how to populate the span with the message after it's been referenced.
So there's a list of items and each item row has their own button which triggers an API with the id associated with that item. Depending on the API response, i want to update the span tag with the response message, but only for that item
When the list is created the items are looped thru and each item includes this
<span ref={'msg' + data.id}></span><Button onClick={() => this.handleResend(data.id)}>Resend Email</Button>
After the API call, I want to reference the specific span and render the correct message inside of it. But I can't figure out how to render to the span at this point of the code. I know this doesn't work, but it's essentially what I am trying to do. Any ideas?
if (response.status === 200) {
this.refs['msg' + id] = "Email sent";

I recommand using state. because string refs legacy (https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs)
const msgs = [
{ id:1, send:false },
{ id:2, send:false },
{ id:3, send:false },
];
this.state = {
msgs
};
return this.state.msgs.map((msg, index) => {
const status = msg.send ? "Email Sent" : "";
<span>{ status }</span><Button onClick={() => this.handleResend(index)}>Resend Email</Button>
});
async handleResend (index) {
const response = await callAPI(...);
if(reponse.status !== 200) return;
const newMsgs = _.cloneDeep(this.state.msgs);
newMsgs[index].send = true;
this.setState({
msgs: newMsgs
})
}

The workaround is set innerText
this.refs['msg' + id].innerText = "Email sent";
But rather than using ref try to use state to update elements inside render.

i was facing with this issue right now and i figured it out this way:
// currentQuestion is a dynamic Object that comes from somewhere and type is a value
const _target = `${currentQuestion.type}_01`
const _val = this[`${_target}`].current.clientHeight // here is the magic
please note that we don't use . after this to call the ref and not using refs to achieve what we want.
i just guessed that this should be an Object that would hold inner variables of the current object. then since ref is inside of that object then we should be able to call it using dynamic values like above...
i can say that it worked automagically!

Related

How to keep the check box checked , when navigating to other page of react pagination

I want to ask , how to keep save the id's of the check boxes in a state , and whenever i switched back to first page it automatically search the element with id and mark check boxes automatically.
and if i unmark the checkbox , it deletes the id from the state.
i am able to think about the logic , but cant able to code,it
Small help ,will leads to solve this problem
While switching to other pages, i am succesfully saving the data ,by updating the state
`
// push all the unique objects (combination of previous state of selectedPayments and data from list)
setSelectedPayments((prevState) => {
var arr = [...prevState, ...list];
var newState = [
...new Map(arr.map((item) => [item.id, item])).values(),
];
return newState;
});
console.log('Selected payments are', selectedPayments);
`
Also , removing the objects , if again the checkbox is unchecked ,and updating the state
`
// pull all the objects , which got unChecked
setSelectedPayments((prevState) => {
var newState = prevState.filter(function (objFromA) {
return !list.find(function (objFromB) {
return objFromA.id === objFromB.id;
});
});
return newState;
});
`
Only facing issue with keeping track of the checked boxes, i have implimented this, this is keeping track of main(parent checkbox).
How to extract the ids saved and check the checkboxes when we naviagete from one page to another
`
let elementId = e.target.id;
if (selectedBoxes.includes(elementId)) {
const newArray = selectedBoxes.filter((e) => e !== elementId);
setSelectedBoxes(newArray);
} else {
setSelectedBoxes((prevState) => {
return [...prevState, elementId];
});
}
`
First i modified the Res Json , so that it set's a property isSelected = true,
by comparing the element from the selectedPayments
inAll check handler , i set the selectedPayments like this
And render using this
This is how ,i solved this problem.
** Better and improved answers are always welcome, please share your views.

react set state - unable to set key name with dot or bracket notation

I am needing to setup state for a dynamic set of data. After the data loads, in the ComponentDidUpdate function I'm iterating through the dataset and then trying to setState for each item in state based on the items Id. I'm choosing id over index because the index isn't accessible once data is displayed and I won't be able to reference it.
When trying to set state I get an error in the IDE that the dot notation or the bracket notation is unexpected.
componentDidUpdate(prevProps, prevState){
if(this.props.report !== prevProps.report && this.props.report !== undefined){
//set checkbox state dynamically for each row
this.props.report.systemSourceList.forEach((item) =>
this.setState((prevState) => ({selectedSourceRowIds : [...prevState.selectedSourceRowIds, {item['sourceId']: true}]}))
}
}
I've tried both item.sourceId and item['sourceId'] but neither work.
I also assigned item.sourceId to a variable and logged it to the console succesfully, but when I plug that variable into the keyName space the id of the source isn't used just id.
this.props.report.systemSourceList.forEach((item) => {
let id = item.sourceId;
console.log(id);
this.setState((prevState) => ({selectedSourceRowIds : [...prevState.selectedSourceRowIds, {id: true}]}))
});
}
The objects being looped through look like:
{
systemId: 13,
facilityId: 4747,
sourceId: "WS010",
}
What am I doing wrong? Is there another way to go about this?
UPDATE
I got it - I needed to place brackets around the actual keyName. Final solution:
this.props.report.systemSourceList.forEach((item) => {
let id = item.sourceId;
console.log(id);
this.setState((prevState) => ({selectedSourceRowIds : [...prevState.selectedSourceRowIds, {[id]: true}]}))
});

How can I create a parent html element by appending sub element from an object?

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?

How to update to an array if element already exists

I'm making a React-Native application. Thanks to everyone's help I could somehow make that work except for toggling YES and NO. Once the user clicks on a button I just want to check if that clicked item data already exists in the state, if so I want to update it. If it does not exist then it should be added to the state as a new Item.
I already coded the above logic, my code is working, but not returning correct output, elements are not adding or updating to the state array properly. How do I fix this code?
I want to generate output like this
[{
chelistid:231,
validiary : "YES",
remark : "Hello"
},{
chelistid:232,
validiary : "NO",
remark : "asddddd"
}]
My code
const [reqData, setReqData] = useState([]);
//Modify yes clicked
const yesClicked = (element) => {
let req = {
"chelistid": element.chelistid,
"validiary": "Yes",
"remark": element.remark
}
createCheckList(req);
}
//Modify no clicked
const noClicked = (element) => {
let req = {
"chelistid": element.chelistid,
"validiary": "No",
"remark": element.remark
}
createCheckList(req);
}
const createCheckList = (data) => {
const index = reqData.findIndex(x => x.chelistid === data.chelistid)
var modifiedArray = reqData
if (index !== -1) {
//Remove the element from the array
modifiedArray.splice(index, 1);
}
setReqData([modifiedArray, data]);
}
The problem is it seems like you are not spreading the array to append the data element. What you are doing by [modifiedArray, data] you are creating an array that contains an array and data something like [[modified array content here], data]. But actually, you want to append to modified array instead. For that, you need to expand the modified array by using ... which is called spread syntax. (Learn here) So, your code would look like:
setReqData([...modifiedArray, data]);

compare an object inside a map function React

I try to render a mapped list of activity based on props. Layout of those props are:
completedActivity:
message:
status:
time:
type:
progressId:
time:
userId:
I need to compare the completedActivity.progressId to another set of props.
logData:
timestamp:
taskRunId:
userName:
I need to see where completedActivity.progressId = logData.taskRunId. If they are the same I need to grab the logData.userName.
Here is the code to render out the activities. It is working just, but I need to add additional info to them. As of right now it will render activity.userId, which is a bind user and not the actual user. If they are not the same, it needs to render the bind user.
const ActivityList = props => {
const activityList = props.data.completedActivity.map(activity => (
<li
class="collection-item"
onClick={() =>
activity.messageDetails
? props.toggleMessageDetails(true, activity.messageDetails)
: false
}
>
<p>{activity.type}</p>
<p className="message">{activity.message}</p>
{/*
displays who last ran that activity and when they did. Need to come up with a better naming solution.
*/}
<div class="whodiditandwhen">
<span>{activity.userId}
</span>
{/*<span>{activity.userId}</span>*/}
<span>{new Date(activity.time).toLocaleString()}</span>
</div>
{/*
this will allow the user to click on the icon and see more detailed info on the activity. NOT ALL ACTIVITES HAVE THIS
*/}
{activity.messageDetails}
</li>
));
return (
<ul className="activity-list">{activityList}
</ul>
);};
Here is a screenshot of how it is rendered now.
Thank you
This is what I tried:
const userNameLog = props.data.completedActivity.map(activity => {
let result = props.logData.find(log => log.taskRunID === activity.progressId)
let userName = ""
if(result === undefined){
userName = activity.userId
} else {
userName = result
}
console.log(userName)
}
)
This works to some degree except it gets rendered multiple times.
I was able to solve this issue inside of the axios call that is getting the data before the page renders it.
export function getActivityUpdates(options, updateProgress) {
axios
.post(URL, qs.stringify(options))
.then(response => {
// Null out saved currentActivityID if no live activity.
handleCurrentActivityStorage(response.data.liveActivity);
let changedIDs = [];
let dataResponse = response.data;
dataResponse.userId = ". . ."
/*
iterate over the completed activity and the log data.
compare progress id to taskRunId.
logData is from highest to lowest id.
completedActivity is also from high to low but puts parents above children.
check and make sure that it has a bind user written to it.
Then if taskrunid is = to or less than the progressId. change it and add it to the changedIDs array.
the ChangedIds array makes sure that ids are not iterated over multiple times.
set the userID to the actual username found in the logs.
*/
dataResponse.completedActivity.forEach(data => {
options.logData.forEach(log => {
//
if (
data.userId === options.bindUsername &&
!changedIDs.includes(data.progressId) &&
(log.taskRunID === data.progressId ||
log.taskRunID < data.progressId)) {
changedIDs.push(data.progressId)
data.userId = log.magUserName;
}
})
});
updateProgress(dataResponse);
// Exit function if a task is not running
if (!response.data.liveActivity.length) {
return;
}
// Get updated data every second until task is complete
setTimeout(
getActivityUpdates.bind(null, options, updateProgress),
1000
);
})
.catch(() => showErrorMessage(options.command));
}

Resources