Trying to populate a option dropdow component using a fetch - javascript

I was looking up how to dynamically populate a dropdown() component I have set up and I am having a hard time getting it to work. The data for the categories is getting pulled (fetched) from a Rails Api I have setup on the backend. I am also getting a 'TypeError: this.props.state is undefined' message as well if that helps with factors to my issue.
So far in my RecipeInput form component this is what I have so far with the dropdown rendered:
//I took out some of the event handlers to try to keep this explanation straight to the point
import React, { Component } from 'react'
import Catagories from './Catagories.js'
class RecipeInput extends Component{
constructor(props){
super(props)
this.state = {
catagories: [],
name:'',
ingredients: '',
chef_name: '',
origin: '',
}
}
componentDidMount(){
let initialCats = [];
const BASE_URL = `http://localhost:10524`
const CATAGOREIS_URL =`${BASE_URL}/catagories`
fetch(CATAGOREIS_URL)
.then(resp => resp.json())
.then(data => {
initialCats = data.results.map((catagory) => {
return catagory
})
this.setState({
catagories: initialCats,
})
});
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit}>
<Catagories catagories={this.state.catagories}/>
<input value='submit' type='submit'/>
</form>
</div>
)
}
}
export default RecipeInput
And here is my actual Catagories component:
import React, { Component } from 'react'
class Catagories extends Component{
constructor(){
super()
}
render(){
let catagories = this.props.state.catagories
let optionItems = catagories.map((catagory,index) =>
<option key={index}>{catagory.name}</option>
)
return (
<div>
<select>
{optionItems}
</select>
</div>
)
}
}
export default Catagories
Who is able to point out my dropdown is not populating based on the code provided?

Use this.props.catagories instead this.props.state.catagories

Related

how to pass value from one component to another in react js

I have one component in which I have one button and I am calling one node js service on that. I am getting a response back from that service and I want to pass that response on next component to display a data there. Below is my component which is doing a node js call.
import { FormGroup } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.css";
import axios from "axios";
export default class Abc extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
}
this.handleClick = this.handleClick.bind(this);
}
handleClick = (e) => {
e.preventDefault();
axios.get(url)
.then(res => {
this.setState({
data: res.data// I need this variable to pass to next component Pqr where I can use it for display purpose.
})
this.props.history.push("/Pqr",{ response:res.data});
})
};
render() {
return (
<form >
<button className="btn btn-info btn-sm" onClick={this.handleClick} style={{ whitespace: 'nowrap' }} >
Launch
</button>
</form>
)
}
}
My Pqr component code is as below.
import React from "react";
export default class ModelLaunch extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
const state = this.props.location.state
return (
<h1>This page will display model Inputs : {state} </h1>
)
}
}
I have solved above problem with other way. Instead of calling a node js service on Abc component I am just redirecting it to new coponent and in new component's componentDidMount() method I am calling a node js service and storind a data in props. In this way I have my data on new copmonent. Below is my updated code in Abc component now.
import { FormGroup } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.css";
import axios from "axios";
export default class Abc extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
}
this.handleClick = this.handleClick.bind(this);
}
handleClick = (e) => {
e.preventDefault();
this.props.history.push("/Pqr");
})
};
render() {
return (
<form >
<button className="btn btn-info btn-sm" onClick={this.handleClick} style={{ whitespace: 'nowrap' }} >
Launch
</button>
</form>
)
}
And in pqr coponent's code as below
import React from "react";
import axios from "axios";
export default class Pqr extends React.Component{
constructor(props)
{
super(props);
this.state = {
data :[]
}
}
componentDidMount(){
axios.get(url).then((res) => {
console.log("res****",res.data)
this.setState({
data:res.data
})
}).catch((err) =>{
console.log("err", err)
})
}
render()
{
return(
<h1>This page will display data</h1>
)
}
}
I see you're changing a route (using react-router?).
Remember that this.setState is async and specific for your component, when you call this.props.history.push('/Pqr'), maybe the this.state.data is not updated yet.
To share this data through different routes in the same react project, I actually know that you can:
Store it on window.localStorage and then get on the next route here have a tutorial
Use react contexts to share data between components (if you're not reloading the page)
Send data through routes with react-router, as explained here
If its not the case, and you just want to pass the property down or above the hierarchy tree, in addition to the comments above, maybe it can help:
As you probably know, react projects are composed of components that are put all together to work in a specific way. In the example below, there are two components (father and child)
import React from 'react';
// We can say that this component is "below" Father
function Child(props) {
return (
<button>
Hey, I'm a button!
</button>
);
}
// We can say that this component is "above" Child
function Father(props) {
return (
<div>
<Child />
</div>
);
}
I couldn't find in the provided code/question, one child component, maybe you forgot to write it?
If the response is "yes", I'll create a fictional component called... FictionalComponent (I'm a Genius!), and pass the data on state as a property named... data.
In order to pass this property, if its the case, you just need to update your render method to look like this:
render() {
return (
<form >
<button
className="btn btn-info btn-sm"
onClick={this.handleClick}
style={{ whitespace: 'nowrap' }}
>
Launch
<FictionalComponent data={this.state.data} />
</button>
</form>
)
}
This way, when this.state.data changes, the FictionalComponent will be re-rendered with the new data value.
But, maybe you want the reverse operation and you need to pass the this.state.data to the component above your Abc, listed there when the button is pressed.
To achieve it you need to have a "Father" component to your Abc, the "Father" component must provide an onDataChanged callback in order to capture the event. This callback will receive the data and handle it.
In this case, I'll create another component to be the component above your Abc. I'll name it... AboveAbcComponent, perfect!
...
class AboveAbcComponent extends React.Component {
constructor(props) {
this.state = {
dataFromChild: null
};
this.onDataChanged = this.onDataChanged.bind(this);
}
onDataChanged(dataReceived) {
console.log("Yey! It works!");
this.setState({ dataFromChild: dataReceived });
}
render() {// Just the passed props changes here
...
<Abc
onDataChanged={this.onDataChanged}
/>
...
}
}
export default class Abc extends React.Component {
constructor(props) { ... } // No changes here
handleClick = (e) => {
e.preventDefault();
axios.get(url)
.then(res => {
this.setState({
data: res.data
});
this.props.onDataChanged(res.data);
this.props.history.push("/Pqr"); // I really didn't understand Why this push is here... but ok
})
};
render() { ... } // No changes here
}
Hope it helps, have fun!

Simple Conditional Routing

i have this react App component
<BreadCrumb>
<Chip/>
<Chip/>
<Chip/>
...
<BreadCrumb/>
<CardContainer>
<Card/>
<Card/>
<Card/>
...
<CardContainer/>
<Button/>
<Stores/>
I need to set as initial page only the component, it takes stores from a JSON and map them in a selection
class Stores extends React.Component {
constructor(props) {
super(props);
this.state = {
stores: [],
};
}
componentDidMount(){
axios
.get(URLS.STORES)
.then(r => r.data)
.then(stores => {
this.setState({
stores
});
console.log(this.state.stores)
});
}
render(){
const options = this.state.stores.map(store => (
<option key={store.storeCode} value={store.storeCode}>
{store.storeName}
</option>
));
return(
<div>
<select>{options}</select>
</div>
);
}
}
i need to render only this component as initial page and after selecting a store to render the entire App, without the stores component, how can i use React router to achieve this?

How to read props on event on React

I creating chat system by React and Firebase.
The data of chat stystem is managemented by Firebase RealTimeDatabase.
Now site here
URL: https://react-chat-b0e8a.firebaseapp.com/
Github: https://github.com/kaibara/React-chat
I'm trying to implement the delete button, but I do not know how to make the child component event read the parent componentthis.props.
As a solution to this, I was thinking to have this.props read in front of render.
But I do not know how to do it.
Can you share the solution to this problem in the following code?
App.js - parenet component
import React, { Component } from 'react'
import firebase from 'firebase/app'
import { firebaseApp,firebaseDB } from './firebase/firebase'
import ChatMessage from './components/ChatMessage'
const messagesRef = firebaseDB.ref('messages')
class App extends Component {
constructor(props) {
super(props)
this.state = {
text : "",
user_name: "",
messages: []
}
}
componentWillMount() {
messagesRef.on('child_added', (snapshot) => {
const m = snapshot.val()
let msgs = this.state.messages
msgs.push({
'text' : m.text,
'user_name' : m.user_name,
'key': snapshot.key
})
console.log({msgs})
this.setState({
messages : msgs
})
console.log(this.state.messages)
})
}
render() {
return (
<div className="App">
<div className="MessageList">
<h2>メッセージログ</h2>
{this.state.messages.map((m, i) => {
return <ChatMessage key={i} messages={m} />
})}
</div>
</div>
)
}
}
export default App
ChatMessage.js - child component
import React,{Component} from 'react'
import { firebaseDB } from '../firebase/firebase'
const messagesRef = firebaseDB.ref('messages')
class ChatMessage extends Component {
onRemoveClick(){
messagesRef.child(this.props.messages.key).remove()
// I want to load `this.props.messages.key` here
}
render(){
return(
<div className="Message">
<p>{this.props.messages.key}</p>
<p className="MessageText">{this.props.messages.text}</p>
<p className="MessageName" style={user}>by {this.props.messages.user_name}</p>
<button className="MessageRemove" onClick={this.onRemoveClick}>削除</button>
</div>
)
}
}
export default ChatMessage
Please lend me your knowledge.
Thank you.
Implement the handler in your parent component and pass the reference down to child component has props
implement onRemoveClick() in App component and pass the handler refrence in `props' to ChatMessage component.
App component:
deleteMessageHandler = (key) =>{
const messages = [...this.state.messages];
messages = messages.splice(key,1);
this.setState({messages:messages});
}
ChatMessage:
render() {
return (
<div className="App">
<div className="MessageList">
{this.state.messages.map((m, i) => {
return <ChatMessage key={i} messages={m} deleteMessageHandler={this.deleteMessageHandler}/>
})}
</div>
</div>
)
}
Note: Don't use the index of the map has a key to the components in the map, its an antipattern, it should be proper unique id's.

Child component does not load event of parent component in React

I creating chat application by React.
In the chat application, there is a field for entering user_name and text.
I thought about managing those data with state, I made onNameChange and onTextChange events.
However, in the code I created, onTextChange was loaded but onNameChange was not loaded.
I know that onTextChange in the same file will be loaded.
Even though the files are different, I thought that data can be exchanged via props if the relationship is between parent and child.
I described the code with such a recognition, but I could not get the results I expected.
How can I pass data from LogoutStateForm.js to user_name in ChatForm.js via onNameChange?
ChatForm.js
import React,{Component} from 'react'
import firebase from 'firebase/app'
import { firebaseApp,firebaseDB } from '../firebase/firebase'
import LogoutStateForm from './LogoutStateForm'
const messagesRef = firebaseDB.ref('messages')
class ChatForm extends Component {
constructor(props){
super(props)
this.onNameChange = this.onNameChange.bind(this)
this.onTextChange = this.onTextChange.bind(this)
this.state = {
user: null,
user_name: "",
text: ""
}
}
componentDidMount(){
firebase.auth().onAuthStateChanged(user => {
this.setState({ user })
})
}
onNameChange(e) {
if (e.target.name == 'user_name') {
this.setState({
user_name: e.target.value
}),
console.log(this.state.user_name);
}
}
onTextChange(e) {
if (e.target.name == 'text') {
this.setState({
text: e.target.value
}),
console.log(this.state.text);
}
}
render(){
return(
<div id='Form'>
{this.state.user ?
<LogoutStateForm onClick={this.onNameChange} />:
null
}
//In order to switch display depending on login availability
<textarea name='text' onChange={this.onTextChange} placeholder='メッセージ'/>
</div>
)
}
}
export default ChatForm
LogoutStateForm.js
import React,{Component} from 'react'
import firebase from 'firebase/app'
class LogoutStateForm extends Component {
constructor(props){
super(props)
}
login() {
const provider = new firebase.auth.GoogleAuthProvider()
firebase.auth().signInWithPopup(provider)
}
componentDidMount(){
firebase.auth().onAuthStateChanged(user => {
this.setState({ user })
})
}
render(){
return(
<div className='logout'>
<input name='user_name' onChange={this.props.onNameChange} placeholder='名前'/>
<button onClick={this.login}>Goggle Login</button>
</div>
)
}
}
export default LogoutStateForm
Please lend me your wisdom.
Thank you.
First, in ChatForm.js, what you render LoginStateForm not LogoutStateForm.
Second, assuming it's supposed to be LogoutStateForm, at ChatForm component you pass onNameChange as onClick to LogoutStateForm.
However, you access the props as onNameChange in LogoutStateForm which is wrong. You should access it as the props name that you give, which is this.props.onClick.
Hope it helps.
In ChatForm.js, you are rendering wrong component, It should be LogoutStateForm.
Second you should access prop which you have passed.
ChatForm.js
<LogoutStateForm onNameChange={this.onNameChange} />
In LogoutStateForm.js
render(){
return(
<div className='logout'>
<input name='user_name' onChange={this.props.onNameChange} placeholder='名前'/>
<button onClick={this.login}>Goggle Login</button>
</div>
)
}
Also, define PropTypes in LogoutStateForm.js for verifying type check.
https://reactjs.org/docs/typechecking-with-proptypes.html

Component with form keeps refreshing page in ReactJS

I'm attempting at creating a simple todo app to try to cement some concepts.
This app gets some previous todos from a .js file with json object. And every time they get clicked they are deleted from the current app.
Now I wanted to add the ability to add todos, first to the current instance of app itself and afterwards to the file to ensure continuity.
My problem arises adding to the app instance.
When using the component with a form, it all goes south.
I tried putting all the component parts in the App.js main file but still the same result, it refreshes after the alert(value) line.
I've also tried changing the order inside the addTodo function and the alert only works if it's the first line of the function, anywhere else the refresh happens before it. So I assumed it's something about using the state of the app component?
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import todosData from "./todosData"
import TodoItem from "./TodoItem"
import TodoForm from './TodoForm'
class App extends Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
const allTodos = this.state.todos
const filtered = allTodos.filter(x => x.id !== id)
const filtered2 = filtered
filtered2.push({id:4,text:"HAKUNA MATATA",completed:false })
this.setState({todos:filtered2})
//added testing so that whenever I delete a todo it adds one with "HAKUNA MATATA"
}
addTodo(value) {
alert(value)
const allTodos = this.state.todos
const lastTodo = this.state.todos.slice(-1)[0]
const newId = lastTodo.id + 1
const newTodo = {id: newId, text: value, completed:false}
allTodos.push(newTodo)
this.setState({todos:allTodos})
}
render() {
const todoItems = this.state.todos.map( item =>
<TodoItem
key={item.id}
item={item}
handleChange={this.handleChange}
/>
)
const text = ""
return (
<div className="todo-list">
{todoItems}
<TodoForm addTodo={this.addTodo}/>
</div>
);
}
}
export default App;
TodoForm.js
import React from 'react'
class TodoForm extends React.Component {
constructor(props) {
super(props)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(event) {
this.props.addTodo(this.input.value)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
</label>
<input type="text" ref={(input) => this.input = input}/>
</form>
)
}
}
export default TodoForm
Can you guys help me with this? From what I understood preventDefault was supposed to prevent this?
Call the preventDefault method on the event the first thing you do in handleSubmit and it should work.
handleSubmit(event) {
event.preventDefault()
this.props.addTodo(this.input.value)
}
You also need to bind the addTodo method to this in the App constructor.
class App extends Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
this.addTodo = this.addTodo.bind(this)
}
// ...
}
In case anyone comes to this answer from Google:
In my case, when I submitted the form, I lazy loaded a component underneath, but it had not been wrapped in <Suspense>. Adding that fixed my issue.

Categories

Resources