Render React component in axios - reactjs

I have just started learning React and I'm trying to make a simple application for searching vacancies using third-party server API. The application consists of form with one input, on submit it sends a request to server using axios, gets a response and must render it.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import "bootstrap/dist/css/bootstrap.css";
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.hh.ru/vacancies/',
headers: {
'User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)',
'HH-User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)'
}
});
const Header = () => {
return <h1>Поиск вакансий на HH.ru</h1>
}
const Vacancies = props => {
return <div>Some Text</div>
}
class SearchForm extends React.Component {
constructor(props) {
super(props)
this.state = {
position: ''
}
this.handlePositionChange = this.handlePositionChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handlePositionChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
var formButton = document.getElementById('form-button');
formButton.disabled = true;
var position = this.state.position;
console.log(position);
if ( position ) {
instance({
method: 'GET',
url: '?text=' + position,
data: {
position: position
}
})
.then(function(response) {
console.log(response.data);
formButton.disabled = false;
})
.catch(function (error) {
console.log(error);
});
} else {
formButton.disabled = false;
}
}
render() {
return (
<form className="form search-form" onSubmit = { this.handleSubmit }>
<div className="form-row">
<div className="form-group col-md-8">
<label htmlFor="position"> Position *
< /label>
<input type="text" className="form-control" name="position" id="position" placeholder="Position" onChange={ this.handlePositionChange } value={ this.state.position } />
</div>
<div className="form-group col-md-4 d-flex flex-column justify-content-end">
<input id = 'form-button'
className = 'btn btn-primary'
type = 'submit'
placeholder = 'Send' / >
</div>
</div>
</form>
)
}
}
class App extends Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-12">
<Header />
<SearchForm />
<Vacancies />
</div>
</div>
</div>
);
}
}
export default App;
I have a problem with rendering <Vacancies />, is it possible to render it dynamically and update data every time on every new request and response from server?

What you want is for Vacancies to get the updated data which is what you get after an API request from SearchForm, in such a case you need to restructure your components and Lift the action up in the parent and pass the data as props to the Sibling components
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import "bootstrap/dist/css/bootstrap.css";
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.hh.ru/vacancies/',
headers: {
'User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)',
'HH-User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)'
}
});
const Header = () => {
return <h1>Поиск вакансий на HH.ru</h1>
}
const Vacancies = props => {
return <div>Some Text</div>
}
class SearchForm extends React.Component {
constructor(props) {
super(props)
this.state = {
position: ''
}
this.handlePositionChange = this.handlePositionChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handlePositionChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
return (
<form className="form search-form" onSubmit = { (e) => this.handleSubmit(e, this.state.position) }>
<div className="form-row">
<div className="form-group col-md-8">
<label htmlFor="position"> Position *
< /label>
<input type="text" className="form-control" name="position" id="position" placeholder="Position" onChange={ this.handlePositionChange } value={ this.state.position } />
</div>
<div className="form-group col-md-4 d-flex flex-column justify-content-end">
<input id = 'form-button'
className = 'btn btn-primary'
type = 'submit'
disabled={this.props.disableSubmit}
placeholder = 'Send' / >
</div>
</div>
</form>
)
}
}
class App extends Component {
state = {
disableSubmit: false;
}
handleSubmit = (e, position) => {
e.preventDefault();
this.setState({disableSubmit : true});
console.log(position);
if ( position ) {
instance({
method: 'GET',
url: '?text=' + position,
data: {
position: position
}
})
.then(function(response) {
this.setState({data: response.data, disableSubmit:false});
})
.catch(function (error) {
console.log(error);
});
} else {
this.setState({disableSubmit : false});
}
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-12">
<Header />
<SearchForm handleSubmit = {this.handleSubmit} disableSubmit={this.state.disableSubmit}/>
<Vacancies data={this.state.data}/>
</div>
</div>
</div>
);
}
}
export default App;
Also while using React you must make sure that you are not modifying the DOM element yourself and handle all Interactions the React way. For instance you can control the disabled state of your submit button using a prop or a state.

Related

how to make child component update when form in parent component submit

Hi I've been at this for two days now and is not getting any solution or answers. It is getting on my nerves and frustrating.
What I am trying to do is to update the list in child component after I hit submit that POST to the db on my api server. The child component is print a list of all records from the DB. When I submit, the child should re-render that list all the record from DB including the one just submitted.
I had to hit page refresh to get the list updated including the newly posted record. I don't want to refresh the page. Just the list (child) component.
I tried every possible solution from Stackoverflow, Google, etc.
Im using React 16.10.
See the entire code below. Tell me what I need to change to make it work.
Im getting headache. Im going to get Tylenol after I post this questions.
Ill start with app.js:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from "react-router-dom";
import './App.css';
import Navigation from './components/Navigation';
import TaskList from './components/tasklist';
import EditTask from './components/listEdit';
import CreateList from './components/listCreate';
class App extends Component {
render() {
return (
<Router>
<div>
<Navigation />
<div className="container">
<Route path="/" exact component={TaskList} />
<Route path="/edit/:id" component={EditTask} />
<Route path="/create" component={CreateList} />
</div>
</div>
</Router>
);
}
}
export default App;
listCreate.js (parent component)
import React, { Component } from 'react';
import TaskList from './tasklist';
import axios from 'axios';
export default class CreateList extends Component {
constructor(props) {
super(props);
this.onChangeListStatus = this.onChangeListStatus.bind(this);
this.onChangeListItem = this.onChangeListItem.bind(this);
this.onChangeListDue = this.onChangeListDue.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
list_status: '',
list_item: '',
list_due: '',
list_created: ''
}
}
onChangeListStatus(e) {
this.setState({
list_status: e.target.value
});
}
onChangeListItem(e) {
this.setState({
list_item: e.target.value
});
}
onChangeListDue(e) {
this.setState({
list_due: e.target.value
});
}
onSubmit(e) {
e.preventDefault();
console.log(`Form submitted:`);
console.log(`Item Status: ${this.state.list_status}`);
console.log(`Item: ${this.state.list_item}`);
console.log(`Item Due: ${this.state.list_due}`);
const newItem = {
list_status: this.state.list_status,
list_item: this.state.list_item,
list_due: this.state.list_due,
};
axios.post('http://localhost:4000/lists/add', newItem)
.then(res => console.log(res.data));
this.setState({
list_status: '',
list_item: '',
list_due: '',
})
}
render() {
return (
<div>
<div style={{marginTop: 10}}>
<h3>Create New Item</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>New Item: </label>
<input type="text"
className="form-control"
value={this.state.list_item}
onChange={this.onChangeListItem}
/>
</div>
<div className="form-group">
<label>Due Date: </label>
<input
type="text"
className="form-control"
value={this.state.list_due}
onChange={this.onChangeListDue}
/>
</div>
<div className="form-group">
<label>Status: </label>
<input
type="text"
className="form-control"
value={this.state.list_status}
onChange={this.onChangeListStatus}
/>
</div>
<div className="form-group">
<input type="submit" value="Create Item" className="btn btn-primary" />
</div>
</form>
</div>
<TaskList reload={"true"}/>
</div>
)
}
}
tasklist.js (child)
import React, { Component } from 'react';
import ItemRow from './itemRow';
import ItemField from './itemField';
import axios from 'axios';
export default class TaskList extends Component {
constructor(props) {
super(props);
this.state = { refreshlist: '',
lists: []
};
}
componentWillReceiveProps(nextProps) {
this.setState({ data: nextProps.data });
}
componentDidMount() {
axios.get('http://localhost:4000/lists/')
.then(response => {
this.setState({ lists: response.data });
})
.catch(function (error){
console.log(error);
})
}
// componentWillReceiveProps(props) {
// this.setState(this.state)
// }
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.total !== prevState.total) {
return (this.setState({ refreshlist: nextProps.refreshlist })) // <- this is setState equivalent
}
// etc...
}
listoftask() {
return this.state.lists.map(function(currentItem, i){
return <ItemRow list={currentItem} key={i} />;
})
}
render() {
return (
<div>
<table className="table table-striped" style={{ marginTop: 20 }} >
<thead>
<ItemField />
</thead>
<tbody>
{ this.listoftask() }
</tbody>
</table>
</div>
)
}
}
update:
This is the api server i use to send data from db
// const dotenv = require("dotenv");
import dotenv from 'dotenv';
import express from 'express';
import cors from 'cors';
// import uuidv4 from 'uuid/v4';
import mongoose from 'mongoose';
const app = express();
const listRoutes = express.Router();
dotenv.config();
const PORT = process.env.PORT || 4000;
const URI_lists = 'mongodb://localhost:27017/lists';
let List = require('./models/task');
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
mongoose.connect(URI_lists,
{useNewUrlParser: true,
useUnifiedTopology: true},
)
.then(() => {
console.log("MongoDB database initial connection established successfully.");
})
.catch((err) => {
console.log("ERROR! Could not connect to Database!");
console.log(err);
});
const connection = mongoose.connection;
connection.on('disconnected',()=> {console.log('lost connection!')});
connection.on('reconnected',()=> {console.log('reconnected to db again!')});
listRoutes.route('/').get(function(req, res) {
List.find(function(err, lists) {
if (err) {
console.log(err);
} else {
res.json(lists);
}
});
});
listRoutes.route('/:id').get(function(req, res) {
let id = req.params.id;
List.findById(id, function(err, list) {
res.json(list);
});
});
listRoutes.route('/update/:id').post(function(req, res) {
List.findById(req.params.id, function(err, list) {
if (!list)
res.status(404).send("data is not found");
else
list.list_item = req.body.list_item;
list.list_status = req.body.list_status;
list.list_due = req.body.list_due;
list.list_created = req.body.list_created;
list.save().then(list => {
res.json('List item updated!');
})
.catch(err => {
res.status(400).send("Update not possible");
});
});
});
listRoutes.route('/add').post(function(req, res) {
let newitem = new List(req.body);
newitem.save()
.then(list => {
res.status(200).json({'list': 'list item added successfully'});
})
.catch(err => {
res.status(400).send('adding new list item failed');
});
});
app.use('/lists', listRoutes);
app.listen( PORT, () => {
console.log('Server is running on Port: ' + PORT);
});
here's my repo on GitHub:
(backend) https://github.com/zenkbaries/todoList
(frontend) https://github.com/zenkbaries/todolistapp
The Child component will re-render when updated props are passed to it. Otherwise it has no need to update.
Looking at your configuration, the Child component only has one prop, and it never changes. Also, the only time you would actually retrieve the updated data from your API is in componentDidMount(), which only triggers after the first initial mount of the component.
For your functionality to work as expected, you'll need to pass in an updated prop each time you submit the form. And upon receiving that update, make a new request to the API.
Without refactoring your code too much, we can do something like this:
In CreateList.js (Parent):
import React, { Component } from 'react';
import TaskList from './tasklist';
import axios from 'axios';
export default class CreateList extends Component {
constructor(props) {
super(props);
this.onChangeListStatus = this.onChangeListStatus.bind(this);
this.onChangeListItem = this.onChangeListItem.bind(this);
this.onChangeListDue = this.onChangeListDue.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
new_item: {},
list_status: '',
list_item: '',
list_due: '',
list_created: ''
}
}
onChangeListStatus(e) {
this.setState({
list_status: e.target.value
});
}
onChangeListItem(e) {
this.setState({
list_item: e.target.value
});
}
onChangeListDue(e) {
this.setState({
list_due: e.target.value
});
}
onSubmit(e) {
e.preventDefault();
console.log(`Form submitted:`);
console.log(`Item Status: ${this.state.list_status}`);
console.log(`Item: ${this.state.list_item}`);
console.log(`Item Due: ${this.state.list_due}`);
const newItem = {
list_status: this.state.list_status,
list_item: this.state.list_item,
list_due: this.state.list_due,
};
axios.post('http://localhost:4000/lists/add', newItem)
.then(res => {
this.setState({
list_status: '',
list_item: '',
list_due: '',
new_item: newItem
})
});
}
render() {
return (
<div>
<div style={{marginTop: 10}}>
<h3>Create New Item</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>New Item: </label>
<input type="text"
className="form-control"
value={this.state.list_item}
onChange={this.onChangeListItem}
/>
</div>
<div className="form-group">
<label>Due Date: </label>
<input
type="text"
className="form-control"
value={this.state.list_due}
onChange={this.onChangeListDue}
/>
</div>
<div className="form-group">
<label>Status: </label>
<input
type="text"
className="form-control"
value={this.state.list_status}
onChange={this.onChangeListStatus}
/>
</div>
<div className="form-group">
<input type="submit" value="Create Item" className="btn btn-primary" />
</div>
</form>
</div>
<TaskList newItem={this.state.new_item}/>
</div>
)
}
}
So we have a newItem object that gets passed to the Child. We're simply using that to identify a change.
taskList.js (Child)
import React, { Component } from 'react';
import ItemRow from './itemRow';
import ItemField from './itemField';
import axios from 'axios';
export default class TaskList extends Component {
constructor(props) {
super(props);
this.state = { refreshlist: '',
lists: []
};
}
componentDidMount() {
axios.get('http://localhost:4000/lists/')
.then(response => {
this.setState({ lists: response.data });
})
.catch(function (error){
console.log(error);
})
}
componentDidUpdate(prevProps){
if(prevProps.newItem !== this.props.newItem){
axios.get('http://localhost:4000/lists/')
.then(response => {
this.setState({ lists: response.data });
})
.catch(function (error){
console.log(error);
})
}
}
listoftask() {
return this.state.lists.map(function(currentItem, i){
return <ItemRow list={currentItem} key={i} />;
})
}
render() {
return (
<div>
<table className="table table-striped" style={{ marginTop: 20 }} >
<thead>
<ItemField />
</thead>
<tbody>
{ this.listoftask() }
</tbody>
</table>
</div>
)
}
}
In the Child component, we introduce the componentDidUpdate() hook which is triggered whenever the child component gets an updated props or state. Then we simply reapply the same logic you had in componentDidMount() to fetch the list data.

Refreshing Component and not all view with react

Im already learning about to fetch data from REST API with react, i have to components (form for submit and a card for get data) both summon from parent component (App), and is working, so i got to push new todo to db and get the news values on my card component, but instead only render cards components, render the all App (incluyed the form component), what am i doing wrong guys?
This is the parent Component
import React, { Component } from 'react';
import './App.css';
import SampleCard from './components/SampleCard';
import Form from './components/Form';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
this.getTasks()
}
getTasks =_=> {
fetch('http://localhost:4000/users')
.then(response => response.json())
.then(data => this.setState({ data: data.data }))
.catch(err => console.log(err))
}
render() {
return (
<div>
<form onSubmit={this.getTasks}>
<Form />
</form>
{this.state.data.map((row, i) => (
<div key={i}>
<SampleCard row={row} />
</div>
))}
</div>
)
}
}
export default App;
This, the form component
import React, { Component } from "react";
class Form extends Component {
constructor(props) {
super(props);
this.state = {
tasks: {
task: '',
status: ''
}
}
}
addTask = _ => {
const { tasks } = this.state
fetch(`http://localhost:4000/users/add?task=${tasks.task}&status=${tasks.status}`)
.catch( err => console.log(err))
}
render() {
const { tasks } = this.state
return (
<div className="Form container mt-3">
<div className="input-group mb-3">
<div className="input-group-prepend">
<span className="input-group-text" id="basic-addon1">
Define Task!
</span>
</div>
<input
type="text"
value={tasks.task}
onChange={e => this.setState({ tasks: { ...tasks, task: e.target.value } })}
className="form-control"
placeholder="Task"
aria-label="Task"
aria-describedby="basic-addon1"
/>
</div>
<div className="input-group mb-3">
<div className="input-group-prepend">
<span className="input-group-text" id="basic-addon1">
Define Status!
</span>
</div>
<input
type="text"
value={tasks.status}
onChange={e => this.setState({ tasks: { ...tasks, status: e.target.value } })}
className="form-control"
placeholder="Status"
aria-label="Status"
aria-describedby="basic-addon1"
/>
</div>
<button
type="Submit"
className="btn btn-outline-success d-flex mr-auto"
onClick={this.addTask}
>
Add
</button>
</div>
);
}
}
export default Form;
and this the card component
import React, { Component } from "react";
export default class SampleCard extends Component {
render() {
return (
<div className="container pt-5">
<div className="col-xs-12">
<div className="card">
<div className="card-header">
<h5 className="card-title">{this.props.row.task}</h5>
</div>
<div className="card-body">
<h4 className="card-title">{this.props.row.created_at}</h4>
{this.props.row.status === 1 ? (
<h3 className="card-title">Pending</h3>
) : (
<h3 className="card-title">Completed</h3>
)}
</div>
</div>
</div>
</div>
);
}
}
You're not preventing default submit behavior. Do it like this
getTasks = (e) => {
e.preventDefault();
fetch('http://localhost:4000/users')
.then(response => response.json())
.then(data => this.setState({ data: data.data }))
.catch(err => console.log(err))
}
Whenever using submit with a form, use e.preventDefault() it prevents refreshing.

New Component is returned but the previous component stays the same in ReactJs

A new component is returned after login, but both the login component and the Home Component are seen on the page. I need to return the Home Componenet without Login Component. I am new to React and still trying to understand return and routes in React.
This is my pages component which returns either Login or Home based on this.state.redirect1.
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import '../../App.css';
import Login from '../Login';
import Home from '../Home';
import Header from './Header';
import Footer from './Footer';
class Pages extends Component {
constructor(props) {
super(props)
this.state = {
redirect: false,
}
}
handleClick() {
this.state.redirect = true;
console.log(this.state.redirect);
}
changeRedirect =() =>{
this.state.redirect = true;
console.log(this.state.redirect);
this.forceUpdate()
}
renderRedirect = () => {
if(this.props.redirect1){
return <Home/>
}
else{
return <Login/>
}
}
render() {
return (
<div className="mh-100 PgWidth">
{this.renderRedirect()}
</div>
)
}
}
export default Pages
Below is my Login, Home and App Components
Login.js
import React, { Component } from 'react'
import Axios from 'axios';
import Pages from './common/Pages'
import { Redirect } from 'react-router-dom';
class Login extends Component {
constructor(props) {
super(props)
this.state = {
username: '',
password: '',
redirect: false
}
}
handleUsername = (event) => {
this.setState({
username: event.target.value
})
}
handlePassword = (event) => {
this.setState({
password: event.target.value
})
}
renderRedirect = () => {
if (this.state.redirect) {
console.log("from render redirect");
return <Pages redirect1={this.state.redirect} />
}
}
formSubmitHandler = event => {
let formdata = new FormData();
formdata.append("username", this.state.username);
formdata.append("password", this.state.password);
Axios.post("/auth/local",{
"name":this.state.username,
"password": this.state.password
})
.then(res => {
if (res) {
console.log(res);
this.setState({ redirect: true });
}
})
event.preventDefault() // used to keep the form data as entered even after the submit
}
render() {
const { username, password } = this.state
return (
<div className="p-5">
{ this.renderRedirect() }
<h3>Sign-In</h3>
<form onSubmit={this.formSubmitHandler}>
<div className="form-group row">
<label htmlFor="inputEmail3" className="col-sm-2 col-form-label">Username</label>
<div className="col-sm-10">
<input type="text" value={username} onChange={this.handleUsername}
className="form-control" id="inputEmail3" placeholder="Username" />
</div>
</div>
<div className="form-group row">
<label htmlFor="inputPassword3" className="col-sm-2 col-form-label">Password</label>
<div className="col-sm-10">
<input type="password" value={password} onChange={this.handlePassword}
className="form-control" id="inputPassword3" placeholder="Password" />
</div>
</div>
<div className="form-group row">
<div className="col-sm-2">Checkbox</div>
<div className="col-sm-10">
<div className="form-check">
<input className="form-check-input" type="checkbox" id="gridCheck1" />
<label className="form-check-label" htmlFor="gridCheck1">
Example checkbox
</label>
</div>
</div>
</div>
<div className="form-group row">
<div className="col-sm-10">
<button type="submit" onClick={this.formSubmitHandler} className="btn btn-primary">Sign in</button>
</div>
</div>
</form>
</div>
)
}
}
export default Login
Home.js
import React, { Component } from 'react'
import '../App.css';
export class Home extends Component {
componentDidMount(){
console.log("home component mount");
}
render() {
return (
<div>
<h1>The page has been routed</h1>
</div>
);
}
}
export default Home
App.js
import React, { Component } from 'react';
import './App.css';
import Header from './components/common/Header';
import Footer from './components/common/Footer';
import Pages from './components/common/Pages';
class App extends Component {
render() {
return (
<div className="App container-fluid bg-light w-75">
<div className="row justify-content-md-center">
<div className="col m-0 p-0">
<Header/>
<div className="">
<Pages/>
</div>
<Footer/>
</div>
</div>
</div>
);
}
}
export default App;
Issue is in this line:
{ this.renderRedirect() }
Once redirect will be true, it will render the Home page first then the Login component.
Solution to you problem is: Manage the redirect bool in Page component only, and pass a function to update to Login component to update its value and decide the component based on that.
Changes:
1- defined redirect: false in Pages component.
2- A function to change its value in Pages component:
updateValue = (value) => {
this.setState({ redirect: true })
}
3- Pass function to Login component:
renderRedirect = () => {
if(this.props.redirect1) {
return <Home/>
}
else{
// =====> here
return <Login updateValue={this.updateValue} />
}
}
4- After successful Login call this function and render Home Component:
formSubmitHandler = event => {
event.preventDefault();
let formdata = new FormData();
formdata.append("username", this.state.username);
formdata.append("password", this.state.password);
Axios.post("/auth/local",{
"name":this.state.username,
"password": this.state.password
})
.then(res => {
if (res) {
// =======> here
this.props.updateValue(true)
}
})
}
5- Remove this line from Login Component:
{ this.renderRedirect() }
Problem with current code:
You are managing the login session using state variable, so after refreshing the page it will again show the login page not home page. So better to store the value in localStorage and read its value in page component to decide the initial value of redirect.
Suggestion:
Instead of deciding the route/component using boolean, better to use react-router for better structuring/managing the app.
Try return it in render:
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import '../../App.css';
import Login from '../Login';
import Home from '../Home';
import Header from './Header';
import Footer from './Footer';
class Pages extends Component {
constructor(props) {
super(props)
this.state = {
redirect: false,
}
}
handleClick() {
this.state.redirect = true;
console.log(this.state.redirect);
}
changeRedirect =() =>{
this.state.redirect = true;
console.log(this.state.redirect);
this.forceUpdate()
}
render() {
if(this.props.redirect){
return (
<div className="mh-100 PgWidth">
<Home/>
</div>
)
} else {
return (
<div className="mh-100 PgWidth">
<Login/>
</div>
)
}
}
export default Pages;
You can do it like this
<div className="mh-100 PgWidth">
{this.props.redirect1&&<Home/>}
{!this.props.redirect1&&<Login />}
</div>
But, the best way to do this is using react router and managing the global react state

not able to share state between two components in react

I have two components-AskQuestion and SingleQuestion
I want to pass the data from AskQuestion to SingleQuestion. How to make this.state.form content available in SingleQuestion component.
AskQuestion.jsx
import React, { Component } from 'react';
import EditableTagGroup from '../EditableTagGroupComponent/EditableTagGroup';
import { createHashHistory } from 'history'
const history = createHashHistory();
class AskQuestion extends Component {
constructor(props) {
super(props)
this.state = {
form: {
Title: '',
Content: '',
Tags: sessionStorage.getItem("TG"),
}
};
this.onChange = this.onChange.bind(this);
this.changeHandler = this.changeHandler.bind(this);
this.submitHandler = this.submitHandler.bind(this);
}
changeHandler(e) {
e.persist();
let store = this.state;
store.form[e.target.name] = e.target.value;
this.setState(store);
}
submitHandler(e) {
e.preventDefault();
fetch('cons/ques/create', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"Request": {
"RequestInfo": {
"userId": "2"
},
"RequestPayload": {
"Questions": [
{
"questionId": 0,
"questionTitle": this.state.form.Title,
"isAnswered": false,
"questionContent": this.state.form.Content,
"tags": [{
"tagId": 1,
"tagName": "Java",
"tagUsage": 1
}]
}
]
}
}
}
)
}).then(res => {
console.log(res);
this.redirect();
return res;
}).catch(err => err);
}
redirect = () => {
this.props.history.push('/SingleQuestion');
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
render() {
const { form } = this.state;
return (
<div className="container">
<h2>ASK A QUESTION</h2>
<form onSubmit={this.submitHandler}>
<div className="form-group">
<label htmlFor="Title">Title:</label>
<input name="Title" type="text" className="form-control" id={this.state.form.Title} placeholder="Enter Title" onChange={this.changeHandler} />
</div>
<div className="form-group">
<label htmlFor="Content">Content:</label>
<textarea type="Content" className="form-control" id={this.state.form.Content} placeholder="Content" name="Content" style={{ height: "300px" }} onChange={this.changeHandler}></textarea>
</div>
<div className="form-group">
<label htmlFor="Tags">Tags:</label>
<EditableTagGroup />
</div>
<button type="submit" className="btn btn-default">Post Question</button>
<button type="submit" className="btn btn-default">Discard</button>
</form>
</div>
)
}
}
export default AskQuestion;
SingleQuestion.jsx
import React, { Component } from 'react';
import './SingleQuestion.css';
class SingleQuestion extends Component {
constructor(props) {
super(props)
this.state = {
};
}
render() {
return (
<div class="question-container col-lg-10">
<div class="question-icons pull-left">
<div class="rating">
<i class="button rating-up fa fa-thumbs-o-up" aria-hidden="true"></i>
<span class="counter">0</span>
<i class="button rating-down fa fa-thumbs-o-down" aria-hidden="true"></i>
</div>
</div>
<div class="result-link pull-left" style={{ paddingLeft: "30px", paddingTop: "55px" }}>
<h1>{this.props.Title}</h1>
</div>
</div>
)
}
}
export default SingleQuestion;
I saw posts like how to share state but didn't help me. mostly i saw something like this
<SingleQuestion callback=*****/>
if I do like that where ever I use this <SingleQuestion ------/> that component will be rendered which i don't want to do. I am new to reactjs please
help me in this..
Thanks in advance!!
This is an example to pass data between parallel components in reactjs
// App.js
import React, { Component } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import SingleQuestion from './SingleQuestion';
import AskQuestion from './AskQuestion';
class App extends Component {
state = {
formData: null
}
callbackFormData = (formData) => {
console.log(formData);
this.setState({formData: formData});
}
render() {
return (
<Switch>
<Route path='/askQuestion' render={() => <AskQuestion callbackFormData={this.callbackFormData}/> } />
<Route path='/singleQuestion' render={() => <SingleQuestion formData={this.state.formData}/>} />
</Switch>
);
}
}
export default App;
//AskQuestion
import React, { Component } from "react";
import { withRouter } from 'react-router-dom';
class AskQuestion extends Component {
redirect = () => {
this.props.history.push("singleQuestion");
};
submitHandler = () => {
let title = document.getElementById('title').value;
if(title !== '')
this.props.callbackFormData(title);
this.redirect();
}
render() {
return (
<React.Fragment>
<input id="title" />
<button onClick={this.submitHandler}>Post Question</button>
</React.Fragment>
)
}
}
export default withRouter(AskQuestion);
// SingleQuestion.js
import React, { Component } from "react";
class SingleQuestion extends Component {
render() {
return <h1>Title:- {this.props.formData}</h1>;
}
}
export default SingleQuestion;
i hope it helps!
If you want to use state form in SingleQuestion component after called redirect, try this.
redirect = () => {
this.props.history.push('/SingleQuestion', {
form: this.state.form
});
}
After then check console.log(this.props.history.location.state.form)

Why is my method Render Props of the React Component not working?

I have a problem. I'm trying do the method Render Prop but it not is working.
My project is: It has to render some names of ComponentDidMount, and I can get it to do the filter and to filter the names. But I passed the function filter for a component, and do the Render Prop.
I pass it here:
import React from 'react';
import './Body.css';
import { Link } from "react-router-dom";
import axios from 'axios';
import Filter from './Filter';
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
employee: []
}
}
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({ employee: response.data }));
}
getName = (filter) => {
const { employee, add } = this.state;
return employee.filter(employee => employee.name.includes(filter)).map(name => (
<div className='item' key={name.id}>
<Link className="link" to={`/user/${name.id}`}>
<div key={name.id}>
<img className="img" alt="imgstatic"
src={`https://picsum.photos/${name.id}`}
/>
</div>
<h1 className="name2"> {name.name} </h1>
</Link>
</div>
));
};
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
}
render() {
return (
<div>
<h4 className="manager"> Hello {this.props.currentManager}, here be all employees available for change. </h4>
<div className="body">
{this.getName()}
</div>
<div className='input'>
<Filter render={this.getName} />
</div>
</div>
)
}
}
export default Body;
And here I get him:
import React from 'react';
class Filter extends React.Component {
constructor() {
super();
this.state = {
input: ''
}
}
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
console.log();
console.log(this.state.input)
}
render() {
return (
<div>
<input placeholder='Search name here' type="text" onChange={this.getValueInput} />
</div>
)
}
}
export default Filter
But something's not working...
Can someone help me?
You are not at all using the render prop being supplied to the Filter component. Also the objective of render prop is to render the data, go using this.getName() inside the render Body Component isn't correct either(for one you are not passing the filter value to the getName). You would use it like
import React from 'react';
import './Body.css';
import { Link } from "react-router-dom";
import axios from 'axios';
import Filter from './Filter';
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
employee: []
}
}
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({ employee: response.data }));
}
getName = (filter) => {
const { employee, add } = this.state;
return employee.filter(employee => employee.name.includes(filter)).map(name => (
<div className='item' key={name.id}>
<Link className="link" to={`/user/${name.id}`}>
<div key={name.id}>
<img className="img" alt="imgstatic"
src={`https://picsum.photos/${name.id}`}
/>
</div>
<h1 className="name2"> {name.name} </h1>
</Link>
</div>
));
};
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
}
render() {
return (
<div>
<h4 className="manager"> Hello {this.props.currentManager}, here be all employees available for change. </h4>
<div className='body'>
<Filter render={this.getName} />
</div>
</div>
)
}
}
export default Body;
and Filter as
import React from 'react';
class Filter extends React.Component {
constructor() {
super();
this.state = {
input: ''
}
}
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
console.log();
console.log(this.state.input)
}
render() {
return (
<React.Fragment>
{this.props.render(this.state.input)}
<div className='input'>
<input placeholder='Search name here' type="text" onChange={this.getValueInput} />
</div>
</React.Fragment>
)
}
}
Note React.Fragment is available from v16.2.0 onwards and if you are not using the relevant version replace React.Fragment with <div>

Resources