import React from 'react';
import axios from 'axios';
class App extends React.Component {
state = { advice: '' };
componentDidMount() {
this.fetchAdvice();
}
fetchAdvice = () => {
axios.get('https://api.adviceslip.com/advice')
.then((response) => {
const { advice } = response.data.slip;
this.setState({advice});
})
.catch((error) => {
console.log(error);
})
}
render () {
const { advice } = this.state;
return (
<div className="app">
<div className="card">
<h1 className="heading">{advice}</h1>
<button className="button" onClick={this.fetchAdvice}>
<span>Give Me Advice!</span>
</button>
</div>
</div>
);
}
}
export default App;
According to your comment, you are calling App with
<li> <Link href="#advice"> <NavLink onClick={App}>Get an Advice</NavLink> </Link> </li>
The way that you are calling App is wrong. This binds the App as a function to the event onClick. This is not the way to use a React Class Component or React Router Link.
Provide a to parameter value and map it to App in the Route using the Switch statement.
For reference, checkout this example
https://reactrouter.com/web/example/basic
You can't assign a react component to an onClick event. Or i should say to any events. You can't call components like callback functions. That's wrong. React does not support routing with URL change by itself. You should use a third party library called react-router-dom. Here's a link to get you started with react router. https://reactrouter.com/web/guides/quick-start
Related
I wanted to check how to react does reconciliation so I updated the inner HTML of id with the same text. Ideally, it shouldn't update the dom but it is paint reflashing in chrome.
I have tried paint reflashing in chrome it is showing green rectangle over that same text
import React from 'react';
function App() {
return (
<div >
<p id="abc" key="help">abc is here</p>
<button onClick={function () {
// document.getElementById("may").innerHTML = "";
document.getElementById("abc").innerHTML = "abc is here";
}} > Btn</button>
</div>
);
}
export default App;
Expected result should be that paint reflashing shouldn't happen but it is happening.
You are not using React here to update the text of your p tag but directly updating the DOM with JavaScript.
So React reconciliation algorithm doesn't even run here.
In React, the output HTML is a result of the state and the props of your component.
When a change in state or props is detected, React runs the render method to check if it needs to update the DOM. So, in order to do this check, you need to store the parameters that determine your view in state or props.
Given your example, we could save the text you want to show in the p tag in the state of your component (using hooks):
import React, { useState } from 'react';
function App () {
const [text, setText] = useState('abc is here');
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => setText('abc is here') }>Btn</button>
</div>
);
}
}
export default App;
If you are using a version of React that does not support hooks, you will need to transform your functional component into a class to use state:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = { text: 'abc is here' };
}
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => this.setState({ text: 'abc is here' }) }>Btn</button>
</div>
);
}
}
export default App;
I have to create a kind of news web app. I'm using NewsAPI.org to make API calls.I have a list of news channels in one page and clicking on any one channel should render specific content from that news channel on another page. I have some information on using react-router but I guess that is not used while dealing with the external domain urls. I'm not able to figure out how to implement this. I have used axios here.
import React, { Component } from 'react';
import {connect} from 'react-redux';
import { fetchSourceList } from '../actions';
class SourceList extends Component{
componentDidMount(){
this.props.fetchSourceList();
}
renderList(){
let {lists} = this.props;
let returnHTML = [];
for(let key in lists){
lists[key].map((item)=>{
returnHTML.push(
<li className="list-group-item" key={item.id}>
<a href={item.url}>{ item.name }</a>
</li>
);
});
}
return returnHTML;
}
render(){
return (
<div>
<ul className = "list-group">
<li className = "list-group-item text-light bg-dark"><h3>Sources</h3></li>
{this.renderList()}
</ul>
</div>
);
}
}
function mapStateToProps(state){
return { lists: state.sourceList };
}
export default connect(mapStateToProps, { fetchSourceList })(SourceList);
You have to use a router as follow:
/news/:type
Where news is a route and type is a param.
In news route you'll render always the same component to render the API response, while you'll use type params to switch channel and feed the service you use for fetching API's. This will allow also a user to copy/paste a channel url, or bookmark it.
I'm new to react and trying to create a eCommerce website. For that I have used a url endpoint to map the data.
http://149.129.128.3:3737/search/resources/store/1/categoryview/#top?depthAndLimit=-1,-1,-1,-1
Well, the second level of subcategories, I'm able to implement successfully, but for the third level of category, I'm not able to render perfectly
I'm sending the screenshot of the json response.
And this is what I have implemented till now, the screen shot.
As you can see, from the screenshot, I'm able to implement the top navigation category and even under it. But there is one more sub level of category-- e.g: under girls section, there are more sub categories which I'm unable to implement.
My code for the same:
topNavigation.js
import React, { Component } from 'react';
import axios from 'axios';
import SubMenu from './subMenu';
class Navigation extends Component {
state = {
mainCategory: []
}
componentDidMount() {
axios.get('http://localhost:3030/topCategory')
.then(res => {
console.log(res.data.express);
this.setState({
mainCategory: res.data.express.catalogGroupView
})
})
}
render() {
const { mainCategory } = this.state;
return mainCategory.map(navList => {
return (
<ul className="header">
<li key={navList.uniqueID}>
<a className="dropbtn ">
{navList.name}
<ul className="dropdown-content">
<SubMenu below={navList.catalogGroupView}/>
</ul>
</a>
</li>
</ul>
)
})
}
}
export default Navigation;
subMenu.js
import React, { Component } from 'react';
import SubListMenu from './subListMenu';
class SubMenu extends Component {
render() {
const {below} = this.props;
return below.map(sub => {
return (
<li key={sub.uniqueID}>
<a>
{sub.name}
<ul>
<SubListMenu subBelow={this.props.below}/>
</ul>
</a>
</li>
)
})
}
}
export default SubMenu;
subListMenu.js
import React, { Component } from 'react';
class SubListMenu extends Component {
render() {
const {subBelow} = this.props;
console.log(subBelow)
return subBelow.map(subl => {
return (
<li key={subl.uniqueID}> <a>{subl.name}</a></li>
)
})
}
}
export default SubListMenu;
Can anyone help me in resolving this issue. I don't know where I'm getting it wrong. I would grateful if someone could guide me on this.
You need is the recursive call to the component. So for example, for the third level you can again call the SubMenu something like this. this will again call the Submenu component and bind your third level (not just third 4,5... etc).
Note: I have not tested this code.
return subBelow.map(subl => {
return (
<React.Fragment>
<li key={subl.uniqueID}> <a>{subl.name}</a></li>
{subl.catalogGroupView !== undefined && <SubMenu below={subl.catalogGroupView} />}
</React.Fragment>
)
});
Let say I have this code (from Create React App):
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
componentDidMount() {
fetch('manifest.json').then(r => r.json()).then(data => {
console.log(data);
}).catch(e => console.log(e));
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;
How to test componentDidMount and get 100% code coverage?
I've used nock and isomorphic-fetch but, is there a better alternative?
Thanks.
You can pass fetch into your component as a prop so in your tests you can pass a mocked version of fetch
example Class:
class Foo extends React.Component {
componentDidMount () {
const { fetch } = this.props
... your fetch code ...
}
}
}
When you are using the component, you pass in real fetch:
const fetch = window.fetch // or however you're importing it
const UsesFoo = () => (
<Foo fetch={fetch} />
)
Now your test: (assuming jest and shallow rendering)
const fakeFetch = jest.fn()
it('Should mock fetch', () => {
const res = shallow(<Foo fetch={fakeFetch} />)
})
fetch you are referring to is a global in create-react-app based projects. Infact, afaik, they use isomorphic-fetch internally to expose this(as a poly-fill for legacy browsers). Since create-react-app comes with jest, you can use jest to mock the global before starting your tests.
Use the standard setupTests.js module to mock any global variable, like fetch.
You can use the global name space to access/mock them.
global.fetch = jest.fn();
I've been struggling with this for a couple days, and any help would be appreciated.
In this component, I have tried to do an HTTP call to my server and database. After parsing the response, using JSON.parse, I am getting back a correctly formed JSON object. I then want to map through that object and for each return a new component (called HistoryItem).
The code below attempts to do this by placing the object into the component state, but it is causing an infinite refresh loop. Previously I had tried a functional component.
The original iteration of this component did work. But it pulled a static JSON object from my client side files. Therefore, I am confident code works without the http call.
It seems to me I am doing something wrong with the async, which is disallowing the JSON object received asynchronously from being rendered.
Below is the main component. Note the component imports the username from redux. This feeds the HTTP call, so that it retrieves only records associated with the logged in user. Again, everything looks fine on the server/database end...
import React, {Component} from 'react';
import style from './history.css';
import HistoryItem from './HistoryItem/historyItem';
import data from '../../config/fakermyhistory.json';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import axios from 'axios';
class History extends Component {
constructor(props) {
super(props);
this.state = {
compiledList:[]
}
}
getData(){
this.state.compiledList.map((call, i) => {
const shaded = (call.rated) ? 'lightgrey' : 'white';
console.log("shaded", shaded);
return(
<Link to={`/reviewpage/${call._id}`} key={call._id}
style={{ textDecoration: 'none', color:'lightgrey'}}>
<div style={{backgroundColor:shaded}}>
<hr/>
<HistoryItem call={call}/>
</div>
</Link>
)
})
}
render(){
axios.post('/api/history', {username: this.props.username})
.then((res) => {
const array = JSON.parse(res.request.response);
this.setState({compiledList: array})
console.log("res", array);}
).catch((err) => console.log("err", err));
return (
<div className={style.container}>
<div className={style.historyHeader}>
<div className={style.historyHeaderText}>
Your Call History
</div>
</div>
<div className={style.historyList}>
{this.getData()};
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
username:state.auth.username
};
}
export default connect(mapStateToProps, null)(History);
Thanks in advance if you can help.
Here is another version using it as a functional component. Also doesn't render (although no errors on this one)
import React, {Component} from 'react';
import style from './history.css';
import HistoryItem from './HistoryItem/historyItem';
import data from '../../config/fakermyhistory.json';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import axios from 'axios';
const History =(props)=> {
const getData=(props)=>{
console.log("props", props);
axios.post('/api/history', {username: props.username})
.then((res) => {
const array = JSON.parse(res.request.response);
console.log("array", array);
array.map((call, i) => {
const shaded = (call.rated) ? 'lightgrey' : 'white';
console.log("shaded", shaded);
return(
<Link to={`/reviewpage/${call._id}`} key={call._id}
style={{ textDecoration: 'none', color:'lightgrey'}}>
<div style={{backgroundColor:shaded}}>
<hr/>
<HistoryItem call={call}/>
</div>
</Link>
)
})
}
).catch((err) => console.log("err", err));
}
return (
<div className={style.container}>
<div className={style.historyHeader}>
<div className={style.historyHeaderText}>
Your Call History
</div>
</div>
<div className={style.historyList}>
{getData(props)};
</div>
</div>
)
}
const mapStateToProps = state => {
return {
username:state.auth.username
};
}
export default connect(mapStateToProps, null)(History);
Instead of calling axios in render function, try to invoke it from componentDidMount.
This will help you prevent the infinite loop.
To return the components rendered within the map function, it was necessary to add a "return" command before the map function was called:
return array.map((call, i) => {...