Read public gists from Github and storing values in Reactapp - arrays

I am creating a webapp on react where when you search for a user you can view that users public gists, files and people who have forked their projects.
I was previously able to get values from https://api.github.com/users?since=1234 by looping through the values:
<div>
{
users.map((user, i) => {
return <Usercard
key={users[i].id}
//Virtual DOM needs key prop to keep track of cards
username={users[i].login}
avatar={users[i].avatar_url}
profile={users[i].html_url}
/>
})
}
</div>
However when I use https://api.github.com/gists/public I am a bit lost with the function to read the data from these group of arrays such as files, forks_url, owner properties and so on as I am unable to get the values.
I am calling the data like so:
componentDidMount() {
fetch('https://api.github.com/users')
.then(response => response.json())
.then(users => this.setState({users: users}));
}

In gist URL https://api.github.com/gists/public
The user info is under owner object, if thats what you are looking for.
Pseudo code for rendering gists info in a table:
const renderUserInfo = data.map((value, key) => {
return <tr key={key}>
<td>
{value.owner.login}
</td>
<td>
{value.owner.url}
</td>
</tr>
});
Hope this helps. Let me know if you are still having trouble :D

Related

Django REST Framework Render a result from an hyperlink in API response

I am building an application with a DRF/React stack, and I have a bit of an issue.
I am using a HyperlinkedModelSerializer to be able to easily make a GET request with the link given. On a less optimistic note, I am not able to query my result that easily.
Here is an example of my json object I get from my API
[
{
"url": "http://localhost:8000/session/3/",
"session_start": "01:00:17.234060",
"nb_person": 1,
[...]
"client": "http://localhost:8000/client/1/"
}
]
There is an array I want to render in my template. Althought, I wanted to show the client firstname and lastname I get from my client link.
function GetContactFromAPI(url)
{
var name = ""
axios.get(url).then(response => {
const data = response.data
name = `${data.firstname} ${data.lastnom}`
return name
})
return name
}
return (
<div>
<table>
<thead>
<tr>
<th>Client</th>
<th>Nb Pers.</th>
<th>Starting time</th>
</tr>
</thead>
<tbody>
{sessions.map((session, index) => {
return (
<tr key={index}>
<td>{GetContactFromAPI(session.client)}</td>
<td>{session.nb_person}</td>
<td>{session.session_start}</td>
</tr>
)
})}
</tbody>
</table>
</div>
);
I think I have some misunderstanding on sync/async in order to render the result I need. Do you have an idea to resolve this need?
I was thinking to get an client list and to get client I need. But I wanted to avoid this request that may bigger than needed (I have more than 40K clients).
Thanks in advance
I may need to see your serializer where the client field is located, but what you need to do is create a Nested Relationship for the client field. Try to do this:
client = ClientSerializer()
Refer to the documentation here

Using a function inside .map() in React , does not work properly

I tried using a function inside the map() function but yet it does not give the output required. I will explain the problem after these code sections.
map function code segment
{bookings.map((booking) =>(
<tr>
<th scope = "row">{number++}</th>
<td>{booking.tourId}</td>
<td>{booking.bookingDate}</td>
<td>{booking.arrivalDate}</td>
<td>{booking.country}</td>
<td>{GuideAssigned(booking.tourId)}</td>
<td><Button color="warning" style = {{padding: "5px 5px 5px 5px" , width : "80px" , marginBottom : "8px"}}
onClick = {()=>{
history.push(`/assign-guide/${booking.username}`);
}}
>Assign Guide</Button>
</td>
</tr>
))}
The problem here is that the function gets called only for one tour ID , which is the first one in the table.The function does not get called for the other Tour ID's.Am I doing something wrong here?
Below is the function called
function GuideAssigned(tid){
axios.get(`http://localhost:8070/assignedGuides/get/${tid}`).then((res)=>{
console.log(res.data.guideId);
setGuide(res.data.guideId);
if (typeof guide == 'undefined'){
return "Not Assigned";
}
}).catch((err)=>{
console.log(err);
})
return guide;
}
This works only for the first tour ID which gets passed as you can see below.(Guide Assigned field is same for all).
You can't call asynchronous code in the render of a React component. The reason they are all the same is likely because you've one guide state, set by setGuide(res.data.guideId);, and it's also very likely it's the last mapped element setting this value since it's the last to do a GET request.
You will want to make these GET requests when the bookings array reference updates. Use an useEffect hook for this.
const [guides, setGuides] = useState({});
useEffect(() => {
bookings.forEach(({ tourId }) => {
axios.get(`http://localhost:8070/assignedGuides/get/${tourId}`)
.then(res => {
setGuides(guides => ({
...guides,
[tourId]: res.data.guideId,
}));
})
});
}, [bookings]);
Use the mapped tourId to access the correct guides guideId value.
{bookings.map((booking) =>(
<tr>
...
<td>{guides[booking.tourId]}</td> // <-- access guide id by tour id
...
</tr>
))}

Mapping Double nested JSON - Object not allowed in React Child

I have a json file that looks like this:
"skills":[
{
"type":"Languages",
"skill":[
{
"name":"Python"
},
{
"name":"Java"
},
{
"name":"JavaScript"
},
{
"name":"HTML"
},
{
"name":"Bash"
},
{
"name":"MySQL"
}
]
},
{
"type": "Flavours",
"skill": [
{
"name":"Reactjs"
},
{
"name":"Angularjs"
},
{
"name":"Agile"
},
{
"name":"Waterfall"
},
{
"name":"Kanban"
}
]
},
{
"type": "Technologies",
"skill": [
{
"name":"Jira"
},
{
"name":" BitBucket"
},
{
"name":"Git"
}
]
}
]
},
And i am trying to render it using a nested mapping function:
var skills = this.props.data.skills.map((skills) =>{
var skill = skills.skill.map(function(skill){
return <li key={skill.name}>{skill}</li>
})
return <ul key={skills.type}>{skills}</ul>
})
However it says "Error: Objects are not valid as a React child (found: object with keys {name}). If you meant to render a collection of children, use an array instead."
So i tried it like this:
var skills = this.props.data.skills.map(function(skills){
var skillsArr = []
var skill = skills.skill.map(function(skill){
var skillArr = []
skillArr.push(<li key={skill.name}>{skill}</li>)
return <span>{skillArr}</span>
})
skillsArr.push(<div key={skills.type}><h3>{skills.type}</h3>
<ul>
{skill}
</ul>
</div>);
return <div>{skillsArr}</div>
})
But this too gives me the exact same error, i dont get what is wrong here because if i do a single mapping of just the skill types it works, it is only when i try to render the inner mapped items does this error occur and break my code
This is how i am calling it btw:
<div className="bars">
<ul className="skills">
{skills}
</ul>
</div>
If we are talking about using React, you should think more about how to organize your code in order to follow a proper component structure, that will let clear what you want to render and how to properly split your data and responsibilities.
Looking to your JSON, we have a set of "skills" that have skills inside it (let's call them "innerSkills").
We can easily split it into 3 components, let's think together:
We can have a List that will render all your Skills.
We can have a Skill that will be responsible for rendering each Skill data, inside it, we will need to render the InnerSkills, so let's split it to another component.
We have then InnerSkill, that will be responsible for rendering each innerSkill that we have for each skill.
So, long story short, what we have is:
List -> Skill -> InnerSkills
Great, now that we established the split, let's see how we can make each component responsible for rendering its data.
Let's say we want to simply call <List skills={data} />. Following this, we can then start on the list itself, which would look something like:
const List = ({ skills }) => (
<ul>
{skills.map((skill, i) => (
<Skill key={i} skill={skill} />
))}
</ul>
);
Now that we are looping through all Skills and calling the Skill component for rendering it, we can take a look at how Skill should look, since it will also need to loop through skill.
const Skill = ({ skill }) => (
<li>
<p>Type: {skill.type}</p>
<ul>
{skill.skill.map((innerSkill, i) => (
<InnerSkill key={i} innerSkill={innerSkill} />
))}
</ul>
</li>
);
Great. Now we already have the two loops you need to render all the data, so it's just missing the definition on how each InnerSkill should look like, and we can take a simplified approach and say we just want to render the name, so it could be something like:
const InnerSkill = ({ innerSkill }) => (
<li>
<p>Name: {innerSkill.name}</p>
</li>
);
To summarize this implementation, I made a simple code sandbox so you can See it live! and play around with the components.
I hope this clarifies your question and helps you to think better in the future on how you want to organize stuff, first check how to split, later how to render. Don't try to start rendering everything inside loops because it will get nasty.
There are two things in your code causing this error:
var skills = this.props.data.skills.map((skills) =>{
var skill = skills.skill.map(function(skill){
// should be <li key={skill.name}>{skill.name}</li>
return <li key={skill.name}>{skill}</li>
})
// should be <ul key={skills.type}>{skill}</ul>
return <ul key={skills.type}>{skills}</ul>
})
Assuming you want a single unordered list of all skill names, I'd suggest using the flatMap() function to re-write this as follows:
<div className="bars">
<ul className="skills">
{this.props.data.skills.flatMap((skillGroup) =>
skillGroup.skill.map((skill) => (
<li key={skill.name}>{skill.name}</li>
))
)}
</ul>
</div>

map function with table is not working in React

Here's the code that I want to render table data with map function in the child component:
const serviceFeeTableData = this.props.serviceFeeTableData;
const serviceFeeDataTable = serviceFeeTableData.map(serviceFeeDataRow => {
return (
<tr>
<td>{`${serviceFeeDataRow.periodFrom} - ${serviceFeeDataRow.periodTo}`}</td>
<td>{serviceFeeDataRow.serviceFeeType}</td>
<td>{serviceFeeDataRow.serviceFee}</td>
<td>{serviceFeeDataRow.refundableStatus}</td>
<td>
<Button variant="primary" size="sm">Edit</Button>
<Button variant="danger" size="sm">Remove</Button>
</td>
</tr>
)
})
As you shown here https://jsfiddle.net/EshanRajapakshe/Lvh6q84t/ the problem is that the state of the parent component only contains one element (and also the response seems to contain only one element)
componentWillReceiveProps(nextProps) {
if (nextProps.getServiceFeeData && nextProps.getServiceFeeData.length > 0) {
this.setState({
periodFrom: nextProps.getServiceFeeData.periodFrom,
periodTo: nextProps.getServiceFeeData.periodTo,
serviceFeeType: nextProps.getServiceFeeData.serviceFeeType,
serviceFee: nextProps.getServiceFeeData.serviceFee,
refundableStatus: nextProps.getServiceFeeData.refundableStatus
})
}
}
You should check the URL you are calling to get a list of data (if you are trying to get the list).
Note also that componentWillReceiveProps is deprecated as described here https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
const object = {0: {name: 'Stepan'}, 1: {name: 'Oleg'}} // You can not map Object keys
const array = Object.keys(object); // create array from object
array.map(key => object[key]);

React make table clickable and edit details

How do I make a table row clickable to edit and update details? I'm retrieving these details from pouchdb.
I'm pasting portion of my code below for your evaluation:
this.state = {docs: []}
this.db = this.props.db
componentDidMount () {
this.updateDocs()
this.db.changes({
since: 'now',
live: true
}).on('change', (change) => {
this.updateDocs()
}).on('error', (err) => {
console.error(err)
})
}
updateDocs () {
this.db.allDocs({include_docs: true}).then((res) => {
var docs = res.rows.map((row) => row.doc)
this.setState({docs})
})
}
And the table below:
<div className='table-list'>
<table>
<thead>
<tr>
<th>Registration Type</th>
<th>First Name</th>
<th>Middle Name</th>
</tr>
</thead>
<tbody>
{this.state.docs.map((doc) => <DataRow key={doc._id} doc={doc} {...this.props} />)}
</tbody>
</table>
</div>
class DataRow extends React.Component {
render () {
let {doc} = this.props
return (
<tr>
<td>{doc.RegistrationInfo['registrationtype']}</td>
<td>{doc.RegistrationInfo['firstname']}</td>
<td>{doc.RegistrationInfo['middlename']}</td>
</tr>
)
}
}
I want to be able to click and edit each of the rows.
My first suggestion - do not do this. Editable grids are quite tough components to implement on your own.
Therefore you have some options to choose from:
Use existing frameworks with editable grids: KendoUI, Wijmo, etc. Although they are quite pricely and most of them have quite pure support for react as for now.
There are some standalone grids with editing functionality: ag-grid, react data grid etc. Some of them are free, other paid.
You can develop your own editable grid based on powerfull components like fixed-data-table, react-virtualized, etc. This approach will still will require some coding to be done but will save you a lot of time.
Make your own components as you are trying now.
If you still would like to go with #4 you can do it this way:
4.1. In state store column of the currently edited cell: editingColumn.
4.2. Assign onClick handler on your <td> tags: <td onClick={(ev) => this.onCellClick(ev))}>. In the handler set editingColumn
4.3. In your render replace
<td>{doc.RegistrationInfo['registrationtype']}</td>
with
<td>{this.renderCell('columnName')}</td>.
And renderCell will look something like this:
private renderCell(colName)
{
if(this.state.editingColumn >= 0 && this.state.editingRow >= 0)
{
// Render your editing control here
// Assign to its 'onChange' like event and save changes to this.props.RegistrationInfo[colName];
}
else
{
return this.props.RegistrationInfo[colName];
}
}
This is very rough description but I hope it will help you get on going.

Resources