Displaying from JSON stored in state - reactjs

I'm trying to access an API and pull out information to use in my application. Right now, I'm fetching the data I want to use from the SWAPI, and the object gets stored in state in the way I want it to. However, I'm not able to display the JSON from the saved state. Im sure this is easy, but I haven't been able to figure it out. Here is my code:
import React, { Component } from 'react';
const APIQuery = 'https://swapi.co/api/';
class Searchbutton extends Component {
constructor(props) {
super(props);
this.state = { value: 'planets/1', data: [] };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
/*Funcionality to handle form and state of form*/
/* Changes state of value whenever the form is changed, in realtime. */
handleChange(event) {
this.setState({ value: event.target.value });
}
/* Prevents default formsubmit for now, and logs the state that is saved.*/
handleSubmit(event) {
console.log('Be with you, and this was written:' + this.state.data);
event.preventDefault();
}
handleJson(json) {
JSON.stringify(this.state.data);
}
/*Lifecycle method that fetches from API*/
componentDidMount() {
fetch(APIQuery + this.state.value)
.then(response => response.json())
.then(data => this.setState({ data }));
}
render() {
return (
<div className="search_wrapper">
<form onSubmit={this.handleSubmit}>
<label>
Search:
<input
type="text"
className="search_bar"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="May the Force" />
</form>
{this.state.data}
json goes here
</div>
);
}
}

You need to stringify the data before you show it.
You have a method that does that although it does not return anything and you also never call it.
So you could change it to
handleJson(json) {
return JSON.stringify(this.state.data); // added the return statement
}
and then when you want to display it use
{this.handleJson()}
json goes here
Or you could directly stringify before showing it
{JSON.stringify(this.state.data)}
json goes here

You can use
<pre>{JSON.stringify(this.state.data)}</pre>
Here is an example of it: https://stackblitz.com/edit/react-y8bk6f
One of the good alternate solution I found is this:
https://stackoverflow.com/a/35340052/2630817
Changes to your render method
return (
<div className="search_wrapper">
<form onSubmit={this.handleSubmit}>
<label>
Search:
<input
type="text"
className="search_bar"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="May the Force" />
</form>
<pre>{JSON.stringify(this.state.data)}</pre>
json goes here
</div>
);

Related

Keep getting max update exceeded error but cannot seem to find error in code

I have made forms like this before but I seem to be missing something in this one. I keep getting the error "maximum update depth exceeded error" but I dont see where I am goin wrong and I've spent too much time looking at it. I already tried to change my onChange to include an arrow because others have suggested to do so , but when that happens I cant type in the input boxes. like so
onChange={()=>this.handleChange("username")}
I should note that I only get the error when I try to register the user and not when I type into the input. Here is the full error as well.
at checkForNestedUpdates (react-dom.development.js:23804)
at scheduleUpdateOnFiber (react-dom.development.js:21836)
at Object.enqueueSetState (react-dom.development.js:12468)
at Router.Component.setState (react.development.js:366)
at react-router.js:75
at listener (history.js:156)
at history.js:174
at Array.forEach (<anonymous>)
at Object.notifyListeners (history.js:173)
at setState (history.js:562)
Here is my code, please help.
import React from "React"
class Splash extends React.Component{
constructor(props) {
super(props)
this.state = this.props.user;
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.props.clearErrors();
}
handleSubmit(event) {
event.preventDefault();
this.props.signUp(this.state);
}
handleChange(field) {
return (e) => {
this.setState({ [field]: e.currentTarget.value })
};
}
render() {
return (
<div className="splash-background">
<div className="modal-screeen">
<form className="modal" onSubmit={this.handleSubmit}>
<h2 className="welcom-text"></h2>
<input className="user-input" type="text" placeholder="Name" onChange={this.handleChange("name")} value={this.state.name}/>
<input className="user-input" type="text" placeholder="Email" onChange={this.handleChange("email")} value={this.state.email}/>
<input className="user-input" type="text" placeholder="Create Username" onChange={this.handleChange("username")} value={this.state.username}/>
<input className="user-input" type="password" placeholder="Create Password" onChange={this.handleChange("password")} value={this.state.password}/>
<button>Sign Up</button>
</form>
</div>
</div>
);
}
}
export default Splash
import { connect } from "react-redux";
import { signup, login, clearErrors } from "../../actions/session_actions.js";
import Splash from "./splash";
const mapStateToProps = ({ errors }) => {
return {
errors: errors.session,
user: {
username: "",
password: "",
name:"",
email: "",
},
};
};
const mapDispatchToProps = (dispatch) => {
return {
signUp: (user) => dispatch(signup(user)),
login: (user) => dispatch(login(user)),
clearErrors: () => dispatch(clearErrors()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Splash);
I believe the problem here is the implementation of redux and react state. If you're using redux to manage the form state then I don't think there is a need to also manage that same state with react.
Try something like this, but keep in mind this code isn't tested.
class Splash extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.props.clearErrors();
}
handleSubmit(event) {
event.preventDefault();
this.props.signUp(this.props.user);
}
handleChange(e) {
// here you would have another action to update redux state depending
// on which input has changed. You can grab the input name via e.target.name
}
render() {
return (
<div className="splash-background">
<div className="modal-screeen">
<form className="modal" onSubmit={this.handleSubmit}>
<h2 className="welcom-text"></h2>
<input
className="user-input"
type="text"
placeholder="Name"
name="name"
onChange={this.handleChange}
value={this.props.user.name}
/>
<input
className="user-input"
type="text"
placeholder="Email"
name="email"
onChange={this.handleChange}
value={this.props.user.email}
/>
<input
className="user-input"
type="text"
placeholder="Create Username"
name="username"
onChange={this.handleChange}
value={this.props.user.username}
/>
<input
className="user-input"
type="password"
placeholder="Create Password"
name="password"
onChange={this.handleChange}
value={this.props.user.password}
/>
<button>Sign Up</button>
</form>
</div>
</div>
);
}
}
export default Splash;
When it comes to form data, I find it's easier to manage just with react state. Generally redux is used to manage state that is shared across the whole application/multiple components.
The problem was actually in my route util file. I had an infinite loop of rerouting!

multiple input elements in form - React

It's a simple form which updates the state object onChange and displays that state object when submitted. I was not able to get it to work when there are multiple input elements.
Can anyone tell me what's wrong in this code?
onSubmit works when there's only one input element, but not when there are multiple!
class ReactForm extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.validate = this.validate.bind(this);
this.state = {
name: "",
email: ""
};
}
handleChange(event) {
event.preventDefault();
const name = event.target.name;
const value = event.target.value;
this.setState({
[name]: value
});
}
validate(event) {
event.preventDefault();
console.log(this.state);
}
render() {
return (
<div>
<form onSubmit={this.validate}>
<div>
<input
type="text"
name="name"
value={this.state.name}
onChange={this.handleChange}
/>
<input
type="email"
name="email"
value={this.state.email}
onChange={this.handleChange}
/>
</div>
</form>
</div>
);
}
}
ReactDOM.render(
<ReactForm />,
document.getElementById("root")
);
You need to have a submit button if you have more than 1 input, you can add a hidden one if you want:
<input type="submit" hidden />
Here's a codesandbox: https://codesandbox.io/s/suspicious-almeida-e3f00
And here is the explanation in detail: Why does a FORM with one text INPUT submit on enter while one with two text INPUTs does not?
I really liked the approach of tudor.
Here is a different approach that can remove the state handling as well. But this may require polyfill for IE and Safari. You can use FormData to access the form values.
new FormData(e.target);
Here is the working sandbox link: https://codesandbox.io/s/long-wind-ybl1w
Hope this helps!
Please add an element input and button. Button should have type="submit" for submitting!
It will work!

Render user input in a different component in React

I am currently rendering user input in a div, in the same component as my form. I want to render the user input on another page (in my other component) instead. How can I render the user input in a different component of my app?
Structure of app:
App.js (navigation setup for react router)
3 components: Home.js, ProjectIdeas.js and Projects.js
Planning.js (the component which contains my form and currently renders the user input) is the child of Projects.js. Projects.js is where I want to render the user input.
Planning.js
constructor() {
super()
this.state = {
title: '',
features: '',
details: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
})
}
handleSubmit(event) {
const {
title,
features,
details
} = this.state;
event.preventDefault();
this.setState({ title, features, details });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>{this.state.title}</div>
<div>
<label className="label-title">
Project Title:</label>
<input name="title" id="title" type="text" onChange={this.handleChange}/>
</div>
<div>
<label className="label-features">
Features:</label>
<input name="features" placeholder="Button (directs to contact page), black footer" id="features" type="text" onChange={this.handleChange}/>
</div>
<div>
<label className="label-details">
Other details: </label>
<input name="details" placeholder="Other Details" id="details" type="text" onChange={this.handleChange}/>
</div>
<input class="submit" type="submit" value="Save plan" />
</form>
)
}
}
Projects.js
class Projects extends React.Component {
constructor(props) {
super(props)
this.state = { isEmptyState: true }
}
triggerAddPlanState = () => {
this.setState({
...this.state,
isEmptyState: false,
isAddPlanState: true
})
}
render() {
return (
<div>
{this.state.isEmptyState && <AddPlanButton addPlan={this.triggerAddPlanState} />}
{this.state.isAddPlanState && <Planning />}
</div>
)
}
}
You'll need to store the value outside the component, either in a common parent component or somewhere orthogonal.
If the components share a nearby parent, you can have the parent keep track of the state and pass it down as props to both:
function ParentComponent () {
const [value, setValue] = React.useState();
<ComponentA value={value} onChange={v => setValue(v)} />
<ComponentB value={value} onChange={v => setValue(v)} />
}
There are many other ways to do it if your app doesn't lend itself to the above solution. I'd recommend you consider using a store like Redux or possibly look at React Context. (Your scenario doesn't seem like a good use of context but you could make it work.)
Implementing your own store is not particularly complicated. Here's an example of one way to do it.

React form not filling out with fetched data

I am currently building a react application with a .net core back end. My current issue lies in a view that is meant to edit an article (which is made up of only a title and description). On componentDidMount, I am getting the route param id from the route and retrieving the article from the server with it (I've verified that this works correctly). My issue is that my form is not filling out with the fetched data. I'm of the understanding that since the form fields set to this.state... then they should update as the state updates however this is not what I'm seeing. I believe the issue is may lie with the warning I'm receiving in console:
index.js:2177 Warning: A component is changing a controlled input of
type hidden to be uncontrolled. Input elements should not switch from
controlled to uncontrolled (or vice versa). Decide between using a
controlled or uncontrolled input element for the lifetime of the
component.
I've read the documentation the warning points to and am not seeing how my component violates this.
My component is below in full:
import React, { Component } from 'react';
import CKEditor from 'react-ckeditor-component';
export class ArticlesEdit extends Component {
displayName = ArticlesEdit.name
constructor(props) {
super(props);
this.state = {
title: '',
description: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount () {
const { id } = this.props.match.params;
fetch(`https://localhost:44360/api/articles/${id}`)
.then((article) => {
this.setState({
title: article.title,
description: article.description
});
});
}
updateDescription(event){
this.setState({description: event.target.value});
}
render() {
return(
<form onSubmit={this.handleSubmit} >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Title">Title</label>
<div className="col-md-4">
<input className="form-control" type="text" id="title" name="title" defaultValue={this.state.title} required />
</div>
</div >
<CKEditor activeClass="editor" content={this.state.description} events= {{"change": this.onEditorChange.bind(this) }} />
<input type="hidden" id="description" name="description" value={this.state.description} onChange={this.updateDescription}/>
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
</div >
</form >
);
}
onEditorChange(evt){
var newContent = evt.editor.getData();
this.setState({
description: newContent
});
}
handleSubmit(event) {
event.preventDefault();
const data = new FormData(event.target);
console.log(this.state.title);
// POST request for Add employee.
fetch('https://localhost:44360/api/articles/', {
method: 'PUT',
body: data
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/articles");
})
}
}
You are not parsing the JSON you get as response to your fetch in componentDidMount. If you add .then((response) => response.json()) it should work as expected.
componentDidMount () {
const { id } = this.props.match.params;
fetch(`https://localhost:44360/api/articles/${id}`)
.then((response) => response.json())
.then((article) => {
this.setState({
title: article.title,
description: article.description
});
});
}
You also need to use the value prop instead of the defaultValue prop on your input so that it will have the value of title in your state.
<input
className="form-control"
type="text" id="title"
name="title"
value={this.state.title}
required
/>

Efficient way to init state storage of forms

I'm working on a quite comprehensive form and was wondering if there is a smart way to prevent me of doing the following state initialisation:
class Demo extends React.Component {
state = { firstName = "",
secondName = "" };
//and so on...
render() {
const { firstName, secondName } = this.state;
//and so on
return (
<div>
<Form>
<Form.Input
placeholder="Name"
name="name"
value={firstName}
/>
//and so on
</Form>
</div>
);
}
}
If I don't init the state with empty strings I get the following Warning:
Component is changing an uncontrolled input of type text to be
controlled. Input elements should not switch from uncontrolled to
controlled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component.
What's the state of the art way to deal with this?
Thanks!
Stefan
If you are using value as your state value then it will be undefined in render method in case you don't initialize state. So it is recommended to initialize your state like you're doing because you have controlled inputs. I believe this would help your case
Something like this should work but problem is you need input handlers for each fields which is cumbersome if you have huge form.
class Demo extends React.Component {
constructor(props) {
super(props)
this.state = {
firstName: '',
lastName: '',
submitted: false
};
}
handleFirstName = (e) => {
this.setState({firstName: e.target.value});
}
handleLastName = (e) => {
this.setState({lastName: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true });
const payload = [{this.state.firstName, this.state.lastName}];
this.props.saveData(payload);
}
render() {
return(
<form>
<label>
Firstname:
<input type="text" value={this.state.firstName} onChange={this.handleFirstName} />
</label>
<label>
Lastname:
<input type="text" value={this.state.lastName} onChange={this.handleLastName} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
Better solution for handling form is using lib like react-final-form

Resources