How to submit a form in react - reactjs

I want to know if it is possible to send a form which is not in react component by fetch in react component.
<form action="json.bc" class="form_search" method="post">
<input type="hidden" name="Firstname" value="">
<input type="hidden" name="Familyname" value="">
<!-- ... -->
</form>
<div id="Result"></div>
The form class="form_search"is outside of the <div id="Result"></div>. I want to submit the form in react component.
class App extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
data: []
};
}
componentDidMount() {
fetch("/json.bc", {
method: "POST"
})
.then(response => response.text())
.then(text => {
var Maindata = JSON.parse(text.replace(/\'/g, '"'));
this.setState(
state => ({
...state,
data: Maindata
}),
() => {}
);
})
.catch(error => console.error(error));
}
render() {
const { data } = this.state;
const renderInfo = data.map((item, i) => {
return <span>{item.name}</span>;
});
return <div class="loading_content">{renderInfo}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("Result"));
Actually I want to have another fetch() request in component to submit this form also I can not add the form in component.

Add on Form tag onSubmit event <Form onSubmit={this.handleSubmit}>
you will need a button also inside of Form which will submit the From.
<button>Submit</button>

Related

Simple calculator in React using Post Request

For educational purposes, I am trying to make a simple calculator where you have two input fields for numbers and one select field for the operator. These choices should go to a backend where it will make the calculation and return the final result.
When I press the button, it refreshes the page and it does not show the value returned from the backend.
I tried to bind the objects under this.state but ir gave me this error: Cannot read property 'bind' of undefined
I am new at coding, and not sure what is wrong with it.
class Calculator extends React.Component{
constructor(props) {
super(props)
this.state = {
firstNumber: '',
operators: '',
secondNumber: '',
total: null
}
}
handleChange = e => {
this.setState({[e.target.name]: e.target.value})
}
handleClick = () => {
const inputField = (this.state.firstNumber, this.state.operators, this.state.secondNumber)
fetch('http://localhost:1337/teste', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(inputField)
})
.then(response => response.json())
.then(data => this.setState({ total: data.valor }))
};
render() {
const { firstNumber, operators, secondNumber, total} = this.state
return (
<div>
<form onSubmit={this.handleClick}>
<input
name='firstNumber'
type='number'
min='0'
placeholder='0'
value={firstNumber}
onChange={this.handleChange}
required
/>
<select
name='operators'
value={operators}
onChange={this.handleChange}>
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="%">%</option>
</select>
<input
name='secondNumber'
type='number'
min='0'
placeholder='0'
value={secondNumber}
onChange={this.handleChange}
required
/>
<button type='submit'>Calculate</button>
</form>
<div>{total}</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"><div>
By default the submit button in a form sends the request to the backend and reloads the page, however in react you want to prevent that as you want to submit the form via an API to the backend (and not through the default form submit action).
Add the event as a parameter to the handleClick function like this.
handleClick = (event) => {
Then add event.preventDefault() as the first line inside your handleClick function.
This should prevent the page reloading.

Callback function from child to parent component in React

I am trying to make a chat. But when I send a message I need to refresh page to get data on the page. I have to components Forms and SendMsg.
Parent:
...
import client from '../Utils/Contentful';
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
}
componentDidMount(){
client.getEntries({limit:300, order: 'sys.createdAt', content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg />
</div>
</div>
);
}
}
And child component
...
import client from '../Utils/ContentfulCM';
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const form = e.target;
const data = new FormData(form);
client.getSpace(client.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
},
//... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error)
this.setState({
message: ''
})
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}
I try to add props but not sure about right place for them
Any suggestions?
Update 1
Both components have "import client" (they are different because have uniq accessToken), that's why I can't use them in one component.
Update 2
I've change question according to suggestion below, but still need to refresh page in order to get displayed data.
Parent:
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
this.sendMessage = this.sendMessage.bind(this);
}
componentDidMount(){
client1.getEntries({limit:300, order: 'sys.createdAt', content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
sendMessage(data) {
client2.getSpace(client2.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error)
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg onSendMessage={this.sendMessage}/>
</div>
</div>
);
}
}
And child component
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const { onSendMessage } = this.props;
const form = e.target;
const data = new FormData(form);
// if send message handler was passed, invoke with form data
onSendMessage && onSendMessage(data);
this.setState({
message: ''
})
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}
Define the callback in the parent. Split out the logic of sending the message data from extracting it from the form in the child. The parent's callback receives the message data and sends it, while the child component's function pulls the form data, formats it, calls the callback passed in props, and clears the input field.
parent
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
this.sendMessage = this.sendMessage.bind(this);
}
componentDidMount(){
client.getEntries({limit:300, order: 'sys.createdAt', content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
sendMessage(data) {
client.getSpace(client.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
},
//... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error);
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg onSendMessage={sendMessage} />
</div>
</div>
);
}
}
child
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const { onSendMessage } = this.props;
const form = e.target;
const data = new FormData(form);
// if send message handler was passed, invoke with form data
onSendMessage && onSendMessage(data);
this.setState({
message: ''
});
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}
UPDATE to include syncing capability
Sync API
Using the Sync API with Javascirpt
Updates to parent component:
Add a class instance timer variable to hold interval timer reference
Create functions to handle syncing data calls
Update componentDidMount to sync initial data when the component mounts, and setup data synchronization polling (since this isn't event driven)
Add timer cleanup in componentWillUnmount lifecycle function
Parent
constructor() {
super()
this.state = {
messages: [],
}
this.syncTimer = null;
this.sendMessage = this.sendMessage.bind(this);
}
initialSyncClient = () => client1.sync({
initial: true
limit:100,
order: 'sys.createdAt',
content_type: 'nameTest',
});
syncClient = () => {
const { nextSyncToken } = this.state;
client1.sync({
nextSyncToken
})
.then(this.handleSyncResponse)
.catch(e => {
console.log(e);
});
};
handleSyncResponse = ({ entries, nextSyncToken}) => {
// response shape is a little different,
// response.entries vs. response.items, so need to access correctly
// also need to save nextSyncToken for all subsequent syncs
this.setState({
messages: entries.items,
nextSyncToken,
});
};
componentDidMount(){
// do initial sync
this.initialSyncClient()
.then(this.handleSyncResponse)
.catch(e => {
console.log(e);
});
// setup sync polling, 15 second interval
this.syncTimer = setInterval(syncClient, 15 * 1000);
}
componentWillUnmount() {
// clean up polling timer when component unmounts
clearInterval(this.syncTimer);
}
NOTE: These changes based purely on the contentful documentation, so there may be need of some tweaking to get working as expected, or if you prefer not using arrow functions, etc...
i don't see where you try to add function sendMessage to parent.
You can provide it by props, why not.
in child component, you can do something like this
interface IChildComponentWithSendMessage{
sendMessage
}
export class ChildComponent extends React.Component<IChildComponentWithSendMessage>
and you can provide you messageMethod by props
also you don't have to do
this.sendMessage = this.sendMessage.bind(this)
also i thinkt that could be your problem why you can't provide this method to child/parent component
you can create functions like this:
sendMessage = () => {
}

Calling Fetch in React App.js with Prop Drilling

So I would like to call fetch from a function (submitURL) in App.js. If I create "componentDidMount()" in App.js and call fetch there, it works, but not from submitURL. I believe this is because submitURL is called via prop drilling. How would I call fetch from submitURL?
App.js
class App extends Component {
state = {
channelURL: '',
videos: []
}
submitURL = (value) => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => console.log(json))
this.setState({
channelURL: value
});
}
render() {
console.log(this.state)
return (
<div className="App">
<h1> Title </h1>
<Channel submitURL={this.submitURL} url={this.state.channelURL}/>
<Videos videos={this.state.videos}/>
</div>
);
}
}
export default App;
Channel.js
class Channel extends Component {
state = {
value: this.props.url
}
handleChange = (e) => {
this.setState({
value: e.target.value
});
}
render() {
return (
<div>
<h1> Enter Channel URL </h1>
<form onSubmit={this.props.submitURL.bind(this, this.state.value)}>
URL: <input type="text" name="url" value={this.state.value} onChange={this.handleChange}/>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default Channel;
submitURL = (value) => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => this.setState({
channelURL: json
}))
}

setState called from within functional argument fails to cause render

*edit to provide solution in comments
I have an app that renders 2 components, a SearchBar form and a Table of data. After the app mounts, an api call is made and setState is called, which triggers a render of the Table. It works fine.
The problem comes from the SearchBar component. On submission, the functional argument handleSubmit is called to make an api request and set the new state. SetState should trigger a render but it doesn't. The state is verified and accurate but there is no render.
Here is my code.
App.js
class App extends Component {
constructor(props) {
console.log('app constructor')
super(props)
this.state = {
items: [],
}
}
render() {
console.log('app render')
return (
<div>
<SearchBar onSubmit={this.handleSubmit} />
<Table data={this.state.items} />
</div>
)
}
componentDidMount() {
console.log('app mounted')
fetch('/api/items/?search=initial search')
.then(res => res.json())
.then((data) => {
this.setState({
items: data
})
console.log('state post mount ' + this.state.items.length)
})
}
handleSubmit(e) {
e.preventDefault()
console.log('search bar submitted ' + e.target.elements.searchBar.value)
fetch(`/api/items/?search=${e.target.elements.searchBar.value}`)
.then(res => res.json())
.then((data) => {
this.setState({
items: data
})
console.log('state post submit ' + this.state.items[0].name)
})
}
}
SearchBar.js
export default class SearchBar extends Component {
constructor(props) {
console.log('search bar constructor')
super(props)
this.onChange = this.handleChange.bind(this)
this.onSubmit = this.props.onSubmit.bind(this)
this.state = {
value: ''
}
}
handleChange(e) {
console.log('search bar changed ' + e.target.value)
this.setState({
searchBarValue: e.target.value
})
}
render() {
return (
<form className='form' id='searchForm' onSubmit={this.onSubmit}>
<input type='text' className='input' id='searchBar' placeholder='Item, Boss, or Zone' onChange={this.onChange} />
</form>
)
}
}
Table.js
export default class Table extends Component {
render() {
if (this.props.data.length === 0) {
return (
<p>Nothing to show</p>
)
} else {
return (
<div className="column">
<h2 className="subtitle">
Showing <strong>{this.props.data.length} items</strong>
</h2>
<table className="table is-striped">
<thead>
<tr>
{Object.entries(this.props.data[0]).map(el => <th key={key(el)}>{el[0]}</th>)}
</tr>
</thead>
<tbody>
{this.props.data.map(el => (
<tr key={el.id}>
{Object.entries(el).map(el => <td key={key(el)}>{el[1]}</td>)}
</tr>
))}
</tbody>
</table>
</div>
)
}
}
}
Please set this in a variable, when function initiate:-
handleSubmit(e) {
let formthis=this;
e.preventDefault()
console.log('search bar submitted ' + e.target.elements.searchBar.value)
fetch(`/api/items/?search=${e.target.elements.searchBar.value}`)
.then(res => res.json())
.then((data) => {
formthis.setState({
items: data
})
console.log('state post submit ' + formthis.state.items[0].name)
})
}
AS I said in the comment, Remove this line this.onSubmit = this.props.onSubmit.bind(this) from the SearchBar component and replace this one
<form className='form' id='searchForm' onSubmit={this.onSubmit}>
with
<form className='form' id='searchForm' onSubmit={this.props.onSubmit}>
The problem is when you call bind the onSubmit from the props with the this as you did it is using the context of the SearchBar and not the parent so it sets the response to the state of the Search bar and not the App component which you want that way your items state of the parent component never changes an as such you don't get a re-render
Here is the relevant code for my solution. As harisu suggested, I changed the declaration of the form component. I also added a bind statement in the constructor of the parent.
App.js
class App extends Component {
constructor(props) {
console.log('app constructor')
super(props)
this.handleSubmit = this.handleSubmit.bind(this)
this.state = {
items: [],
}
}
handleSubmit(e) {
e.preventDefault()
console.log('search bar submitted ' + e.target.elements.searchBar.value)
fetch(`/api/items/?search=${e.target.elements.searchBar.value}`)
.then(res => res.json())
.then((data) => {
this.setState({
items: data
})
})
console.log('state post submit ' + this.state.items[0].name)
}
}
SearchBar.js
export default class SearchBar extends Component {
render() {
return (
<form className='form' id='searchForm' onSubmit={this.props.onSubmit}>
<input type='text' className='input' id='searchBar' placeholder='Item, Boss, or Zone' onChange={this.onChange} />
</form>
)
}
}

Updating State in textarea using controlled versus uncontrolled components

I'm trying to update a note and have both an input and textarea. I can update the state in my input just fine. However, only one additional character will update in my textarea. I know that it has to do with textarea being a controlled component and taking a value attribute rather than a defaultValue attribute. However, I can't seem to figure out what I'm doing wrong.
export default class UpdateNote extends Component {
constructor(props) {
super(props);
this.state = {
note: {},
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
submitHandler = (e) => {
e.preventDefault();
const note = {
title: this.state.title,
textBody: this.state.textBody,
};
const id = this.props.match.params.id;
axios
.put(`${URL}/edit/${id}`, note)
.then(response => {
this.setState({ title: '', textBody: '' })
this.setState({ note: response.data })
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div className="notes-list">
<h2 className="your-notes">Edit Note:</h2>
<form className="input-form" onSubmit={this.submitHandler} >
<input type="text" defaultValue={this.state.note.title} name="title" onChange={this.handleChange} />
<textarea type="textarea" value={this.state.note.textBody} name="textBody" onChange={this.handleChange} />
<button type="submit" className="submit-button">Update</button>
</form>
</div>
)
}
}

Resources