Displaying data from API undefined - javascript

I'm trying to display student data and the courses they are enrolled in from a a simple API I made.
Here's the result from the API https://localhost:44309/api/Students
[
{
"id": 1,
"lastName": "Rizal",
"firstName": "Jose",
"courses": [
"C#",
"Javascript",
"CSS"
]
},
{
"id": 2,
"lastName": "Bonifacio",
"firstName": "Andres",
"courses": [
"HTML",
"ASP.NET MVC"
]
},
{
"id": 3,
"lastName": "Sora",
"firstName": "Tandang",
"courses": [
"CSS",
".NET"
]
}
]
Here's my Javascript code
import React from "react"
import { useState, useEffect } from "react";
const StudentList = () => {
const [students, setStudent] = useState([]);
const getData = async () => {
const response = await fetch("https://localhost:44309/api/students");
const data = await response.json()
setStudent(data)
}
useEffect(() => {
getData()
}, []);
return (
<div className="student-list">
<h2>Students and their courses</h2>
{students.map(student => (
<div className="student-preview" key={student.id}>
<p>{ student.fname } { student.lname }</p>
<p>{ student.courses }</p>
</div>
))}
</div>
)
}
export default StudentList
I'm getting an error saying
Uncaught ReferenceError: students is not defined
I wonder what I'm doing wrong.

Something is wrong with your api the code works fine with static data
import React from "react";
import { useState, useEffect } from "react";
const StudentList = () => {
const [students, setStudent] = useState([
{
id: 1,
lastName: "Rizal",
firstName: "Jose",
courses: ["C#", "Javascript", "CSS"]
},
{
id: 2,
lastName: "Bonifacio",
firstName: "Andres",
courses: ["HTML", "ASP.NET MVC"]
},
{
id: 3,
lastName: "Sora",
firstName: "Tandang",
courses: ["CSS", ".NET"]
}
]);
return (
<div className="student-list">
<h2>Students and their courses</h2>
{students.map((student) => (
<div className="student-preview" key={student.id}>
<p>
{student.fname} {student.lname}
</p>
<p>{student.courses}</p>
</div>
))}
</div>
);
};
export default StudentList;

Related

How to append data from a form in React?

I have two input fields here and I would like to create a new cards object by clicking submit. I got as far as displaying the object in the dev tools, but i can't manage to display this data from the input fields as a new card.
I suspect it has something to do with formdata, append and state management.
Does anyone have an idea how this works?
My App.js:
import "./App.css";
import Card from "./components/Card";
import Header from "./components/Header";
import CardComponents from "./components/CardComponents";
import CreateForm from "./components/Form";
export default function App() {
const colorCard = [
{
name: "Card1",
id: "234",
colorCode: "#ccc",
},
{
name: "Card2",
id: "2",
colorCode: "#4c6ef5",
},
{
name: "Card3",
id: "3",
colorCode: "#82c91e",
},
{
name: "Card4",
id: "4",
colorCode: "#12b886",
},
{
name: "Card5",
id: "5",
colorCode: "#00FFFF",
},
{
name: "Card6",
id: "7",
colorCode: "#9FE2BF",
},
{
name: "Card8",
id: "5",
colorCode: "#DE3163",
},
{
name: "Card9",
id: "5",
colorCode: "#50C878",
},
{
name: "Card10",
id: "5",
colorCode: "#40E0D0",
},
];
return (
<main>
<Header />
<CreateForm />
<ul>
{colorCard.map((card) => (
<CardComponents
key={card.id}
color={card.colorCode}
name={card.name}
/>
))}
</ul>
</main>
);
}
Form.js component:
import { useState } from "react";
const CreateForm = () => {
const [value, setValue] = useState("");
const [code, setCode] = useState("");
const submitHandler = (event) => {
event.preventDefault();
const newColorBox = { value, code };
console.log(newColorBox);
};
return (
<form onSubmit={submitHandler}>
<h2>Add Card</h2>
<input
type="text"
required
placeholder="Name your Card"
value={value}
onChange={(event) => setValue(event.target.value)}
></input>
<br></br>
<input
type="text"
placeholder="Name your Color Code"
required
value={code}
onChange={(event) => setCode(event.target.value)}
></input>
<br></br>
<button type="submit">Submit your Card</button>
<p> CardName: {value}</p>
<p>ColorCode: {code}</p>
</form>
);
};
export default CreateForm;
1 Add:
const [cards,setCards]=useState(/*you car insert your initial data in app.js*/)
pass setCards() to form.js and call it inside of submitHandler.

Custom Strapi field shows null

Hi everyone,
I have created a custom field plugin which is connectect to mapbox search API to get locations when the user types.
I have created the component and everything seems to be working fine until the part where I need to send the object to API.
When I want to store the location title in the API it works just fine
{
"id": 1,
"companyName": "Stina",
"location": "South Africa",
"published_at": "2021-02-25T22:31:14.550Z",
"created_at": "2021-02-25T22:29:18.335Z",
"updated_at": "2021-02-28T21:55:15.064Z",
}
So this is the code used to get that:
const updateLocationValue = (locationValue) => {
props.onChange({
target: { name: "location", value: locationValue.title },
});
};
But when I want to send the whole object so I can get the coordinates as well I get null
{
"id": 1,
"companyName": "Stina",
"location": null,
"published_at": "2021-02-25T22:31:14.550Z",
"created_at": "2021-02-25T22:29:18.335Z",
"updated_at": "2021-02-28T21:55:15.064Z",
}
This is the code that I use to send the whole object, and this works just fine in the console log and I am getting the coordinates as well but I can't pass it to Strapi:
const updateLocationValue = (locationValue) => {
props.onChange({
target: { name: "location", value: locationValue },
});
};
I am expecting to see an object just as below so I can get the title and coordinates:
{
"id": 1,
"companyName": "Stina",
"location": {object},
"published_at": "2021-02-25T22:31:14.550Z",
"created_at": "2021-02-25T22:29:18.335Z",
"updated_at": "2021-02-28T21:55:15.064Z",
}
I have registered the field in company.settings.json just as you can see below:
{
"kind": "collectionType",
"collectionName": "companies",
"info": {
"name": "Company",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"attributes": {
"companyName": {
"type": "string"
},
"location": {
"type": "locationsearch",
"columnType": "longtext"
},
"products": {
"collection": "product",
"via": "company"
}
}
}
So the columnType is longtext, I have tried to change it to "object" or "JSON" but it still shows me null
Here is the full code of the component which is working just fine:
import { Label, InputText } from "#buffetjs/core";
const apiToken =
"****************************************";
export default function Location(props) {
const [locationTitle, setLocationTitle] = useState("");
const [suggestions, setSuggestions] = useState([]);
const [suggestionsOpen, setSuggestionsOpen] = useState(false);
const wrapperRef = useRef(null);
const popperEl = useRef(null);
const [cursor, setCursor] = useState(null);
const fetchLocation = (searchText) => {
searchText &&
fetch(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${searchText}.json?access_token=${apiToken}`
)
.then((response) => response.json())
.then((data) => {
const newSuggestions = data
? data.features.map((feature) => ({
title: feature.place_name,
centerCoordinates: feature.center,
}))
: [];
setSuggestions(newSuggestions);
});
};
useEffect(() => {
locationTitle && fetchLocation(locationTitle);
}, [locationTitle]);
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
const updateLocationValue = (locationValue) => {
props.onChange({
target: { name: "location", value: locationValue.title },
});
};
const handleClickOutside = (e) => {
const { current: wrap } = wrapperRef;
if (wrap && !wrap.contains(e.target)) {
setSuggestionsOpen(false);
}
};
const setLocationValue = (location) => {
setLocationTitle(location.title);
updateLocationValue(location);
setSuggestionsOpen(false);
};
return (
<div ref={wrapperRef}>
<Label htmlFor="input">Location</Label>
<InputText
name="input"
onChange={(e) => setLocationTitle(e.target.value)}
placeholder="Riva 16, 21420, Bol, Croatia"
type="search"
value={locationTitle}
onClick={() => setSuggestionsOpen(true)}
/>
<div ref={popperEl} className="options-wrapper">
{suggestionsOpen && (
<div>
{suggestions.map((suggestion) => {
return (
<div
onClick={() => setLocationValue(suggestion)}
key={suggestion.id}
>
{suggestion.title}
</div>
);
})}
</div>
)}
</div>
</div>
);
}
Could anyone recommend any steps to finalize this?
Thanks a lot!

Select dependent on reactjs with react-select

I'm developing an App in ReactJS, and I have a page where I want to show two select, one dependent on the other.
I'm using react-select and #material-ui.
In dates:
[
{
"id": 1,
"name": "202001"
},
{
"id": 2,
"name": "202002"
},
{
"id": 3,
"name": "202003"
},
{
"id": 4,
"name": "202004"
},
{
"id": 5,
"name": "202005"
},
{
"id": 6,
"name": "202006"
},
{
"id": 7,
"name": "202007"
}
]
I have a list of dates that are available to select.
import React, { useState, useEffect } from "react";
import Select from "react-select";
...
const App = () => {
...
const DateA = dates.map((item) => ({
value: item.id,
label: item.name,
}));
const DateB = dates.map((item) => ({
value: item.id,
label: item.name,
}));
const [dateA, setDateA] = React.useState(null);
const [dateB, setDateB] = React.useState(null);
function handleChangeDateA(value) {
setDateA(value);
}
function handleChangeDateB(value) {
setDateB(value);
}
return (
<div className="App">
<div className="col-3">
<Select
classes={classes}
styles={selectStyles}
inputId="DateA"
TextFieldProps={{
label: "DateA",
InputLabelProps: {
htmlFor: "DateA",
shrink: true,
},
placeholder: "DateA...",
}}
options={DateA}
components={components}
value={dateA}
onChange={handleChangeDateA}
/>
</div>
<div className="col-3">
<Select
classes={classes}
styles={selectStyles}
inputId="DateB"
TextFieldProps={{
label: "DateB",
InputLabelProps: {
htmlFor: "DateB",
shrink: true,
},
placeholder: "DateB...",
}}
options={DateB}
components={components}
value={dateB}
onChange={handleChangeDateB}
/>
</div>
</div>
);
};
export default App;
The idea is that the DateB select take dates greater than the ones selected in the DateA select.
How can I do this, suggestions?
Try this
useEffect(() => {
if (condition) {
setDateB(value)
}
}, [DateA])

Pass dynamic content to the react table sub component

I am using react table v6 for grid purposes. I am trying to implement a subcomponent where in the data to this sub component needs to be passed dynamically. This sub component should expand and collapse on click of arrows. I have tried the following , but the sub component is not rendering any data. I am creating a wrapper for this, the data to the subcomponent should be passed dynamically based on the source data.
https://codesandbox.io/s/react-table-row-table-subcompoentn-sk14i?file=/src/DataGrid.js
import * as React from "react";
import ReactTable from "react-table";
import "react-table/react-table.css";
export default class DataGrid extends React.Component {
renderSubComponent = original => {
console.log(original);
return (
original.nested &&
original.nested.map((i, key) => (
<React.Fragment key={key}>
<div>{i.name}</div>
<div>{i.value}</div>
</React.Fragment>
))
);
};
render() {
return (
<ReactTable
data={this.props.data}
columns={this.props.columns}
SubComponent={this.renderSubComponent}
/>
);
}
}
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
columns: [],
allData: []
};
}
componentDidMount() {
this.getData();
this.getColumns();
}
getData = () => {
const data = [
{
firstName: "Jack",
status: "Submitted",
nested: [
{
name: "test1",
value: "NA"
},
{
name: "test2",
value: "NA"
}
],
age: "14"
},
{
firstName: "Simon",
status: "Pending",
nested: [
{
name: "test3",
value: "NA"
},
{
name: "test4",
value: "Go"
}
],
age: "15"
}
];
this.setState({ data });
};
getColumns = () => {
const columns = [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Age",
accessor: "age"
}
];
this.setState({ columns });
};
onClickRow = rowInfo => {
this.setState({ allData: rowInfo }, () => {
console.log(this.state.allData);
});
};
render() {
return (
<>
<DataGrid
data={this.state.data}
columns={this.state.columns}
rowClicked={this.onClickRow}
/>
</>
);
}
}
render(<App />, document.getElementById("root"));
try spread the argument and it will work :
...
renderSubComponent = ({original}) => {
...

React Context initial state is being mutated - JavaScript / React

I am making a guide me section. What this guide me section does - displays an array of processes. Within each process is an array of steps, within each step is an array of options. The user selects an option from one of the steps, it takes them to the next correlating step. If the user selects the option on step 2, it could take them to step 3 or back to step 1. It depends on the id.
With all that said I'm having issues with my Process mutating on me. I'm using React Context as a global state. When a user selects an option, I'm grabbing that id, then filtering the designated object by that id. So I should only be left is that processes with that specific step. What's happening is my initial global state is mutating somehow. I'm missing something here as I'm new to React.
P.s - I'm not using any services at this moment, so I just copied some JSON over to my initial state in context.js
context.js
import React, { Component } from 'react'
// import axios from 'axios'
const Context = React.createContext()
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_PROCESS':
return {
...state,
guides: action.payload
}
default:
return state
}
}
export class Provider extends Component {
state = {
guides: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585x",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
initialGuide: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
dispatch: action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer;
Guides.js
import React, { Component } from 'react'
import { Consumer } from '../../context'
import Process from './Process'
class Guides extends Component {
constructor (props) {
super(props)
this.state = {
contextValue: [],
searchData: props.location.data
}
}
render () {
console.log(this.props.location.data, this.state, 'logging state and props on guides')
// this.state.searchData = this.props.location.data
return (
<Consumer>
{value => {
return (
<React.Fragment>
<div className="content-wrapper">
<h1>Guide Me</h1>
<div className="ms-Grid times--max-width" dir="ltr">
<div className="ms-Grid-row">
<div className="profile--wrapper ms-Grid-col ms-sm12 ms-md12 ms-lg5">
{value.guides.map(item => {
return <Guide key={item.id} guide={item} processValue={value.guides} initialGuide={value.initialGuide}/>
})}
</div>
</div>
</div>
</div>
</React.Fragment>
)
}}
</Consumer>
)
}
}
export default Guides
Process.js
import React, { Component } from 'react'
import GuideSteps from './Guide-Steps'
import { Consumer } from '../../context'
class Process extends Component {
constructor(props) {
super(props)
this.state = {
processName: this.props.guide.processName,
process: this.props.guide,
steps: this.props.guide.steps,
selectedIndex: 0,
selectedStep: '',
processValue: this.props.processValue,
initialGuide: this.props.initialGuide
}
this.displayStep = this.displayStep.bind(this)
}
displayStep = (res, dispatch) => {
this.setState({ selectedStep: res })
}
render() {
const { steps, selectedIndex, process, processName, processValue, initialGuide } = this.state
return (
<Consumer>
{value => {
return (
<div>
<h2 className="profile--sub-header--bold">{processName}</h2>
<GuideSteps
key={this.props.guide.steps[selectedIndex].id}
selectedStep={this.props.guide.steps[selectedIndex]}
stepValue={this.displayStep}
process={process}
processValue={processValue}
initialGuide={initialGuide}
/>
</div>
)
}}
</Consumer>
)
}
}
export default Process
Guide-Steps.js
import React, { Component } from 'react'
import { ChoiceGroup } from 'office-ui-fabric-react/lib/ChoiceGroup'
import { Consumer } from '../../context'
class GuideSteps extends Component {
constructor(props) {
super(props);
this.state = {
process: [],
selectedStep: this.props.selectedStep,
dispatch: '',
processValue: this.props.processValue,
initialGuide: ''
}
this._onChange = this._onChange.bind(this)
}
_onChange = (ev, option) => {
// this.props.stepValue(option.value.nextStep)
const { dispatch , initialGuide } = this.state
let optionArray = option.value.nextStep.split(',')
let processArray = this.state.process.filter(item => {
return item.id === optionArray[0]
})
let stepArray = processArray[0].steps.filter(item => {
return item.id === optionArray[1]
})
console.log(stepArray, processArray, this.state.process, 'logging step array before setting')
processArray[0].steps = stepArray
console.log(stepArray, processArray, this.state.process, 'logging step array after setting')
dispatch({
type: 'SEARCH_PROCESS',
payload: processArray
})
}
render() {
let options = []
{
this.props.selectedStep.options.map(item => {
return options.push({
key: item.text,
text: item.text,
value: item
})
})
}
return (
<Consumer>
{value => {
const { dispatch, guides, initialGuide } = value
this.state.dispatch = dispatch
console.log(value, 'logging initial guide in render')
this.state.process = initialGuide
return (
<div>
<ChoiceGroup
className="defaultChoiceGroup"
options={options}
onChange={this._onChange}
/>
</div>
)
}}
</Consumer>
)
}
}
export default GuideSteps
On change in GuideSteps is where I'm doing the logic for filtering and setting up my new object.
EDIT
This fixed the issue but I think it's too expensive. How would I go about solving this issue without having to reparse the array.
update: (ev, option) => {
const { initialGuide } = this.state
if (option.value.nextStep !== null && option.value.nextStep !== '') {
//split string
const optionArray = option.value.nextStep.split(',')
//filter process array
const processArray = initialGuide.filter(process => {
return process.id === optionArray[0]
})
//filter step array
const stepArray = processArray[0].steps.filter(
item => item.id === optionArray[1]
)
if(stepArray.length > 0 && stepArray !== null) {
//get a copy of the process array so original is not mutated by the steps
let stringC = JSON.stringify(processArray)
let stringD = JSON.parse(stringC)
stringD[0].steps = stepArray
//issue might be here visually where setting the state happens quickly, therefore radio button visual does not display in time.
setTimeout(() => {
this.setState({ guides: stringD })
}, 200)
}
}
},
this.state.process = initialGuide
let processArray = this.state.process.filter...
processArray[0].steps = stepArray
So it looks like you're mutating initialGuide via reference.

Categories