I am currently learning react.js and javascript. Today I started to have a look at the Rest API of wordpress. I managed to load and map post from a specific category on to my page. Now I would like to load a different categories on click. However I am kind of stuck. The data for each category can be called via the following link:
http://127.0.0.1/reactwp/wp-json/wp/v2/posts/?filter[category_name]=travel
http://127.0.0.1/reactwp/wp-json/wp/v2/posts/?filter[category_name]=categoryXY
I would like to change the category name based on an onclick event on a button. So for example if someone clicks on the category Car all posts from the WP Category Car are loaded. So I need an onclick event that changes the last part of the url to call the data from this specific category. This is my code so far.
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Request from 'superagent';
import _ from 'lodash';
class TestComponent extends Component {
constructor() {
super();
this.state = {};
}
componentWillMount () {
var url = "http://127.0.0.1/reactwp/wp-json/wp/v2/posts/?filter[category_name]=travel";
Request.get(url).then((response) => {
this.setState({
category: response.body,
total: response
});
});
}
render() {
var category = _.map(this.state.category, (website) => {
return (
<div>
<h2>{website.title.rendered}</h2>
<p>{website.content.rendered}</p>
</div>
);
});
return (
<div>
<button>Click to load category1</button>
<button>Click to load category2</button>
<ul>{category}</ul>
</div>
);
}
}
export default TestComponent;
Thanks for your help.
For anyone that faces a similar problem. I solved it by myself. However be aware this is not a perfect solution since redux is not implemented. I simply used my link structure (params) to call the right category from the Wordpress Rest API
class TestComponent extends Component {
constructor() {
super();
this.state = {};
}
componentWillReceiveProps() {
let category = (this.props.params.data);
var url = "http://127.0.0.1/reactwp/wp-json/wp/v2/posts/?filter[category_name]="+category+"";
Request.get(url).then((response) => {
this.setState({
category: response.body,
total: response
});
});
}
render() {
var category = _.map(this.state.category, (website) => {
return (
<div>
<WebsiteItem title={website.title.rendered} description={website.content.rendered} image={website.better_featured_image.source_url} link={website.acf.websitelink}/>
</div>
);
});
return (
<div>
<ul>{category}</ul>
</div>
);
}
}
export default TestComponent;
Related
When changing my id (/movie/:id), i'm re rendering my whole component. Sometimes i have to click 3 or 4 times on my like to have a change and sometimes i have only to click once(but im one component behind).
Here is my code :
import React from "react";
import "../styles/DetailFilm.css"
import {Link} from 'react-router-dom';
const API_IMAGES = 'https://image.tmdb.org/t/p/w500';
class DetailFilm extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.movie_id,
info: {},
recommandation: []
}
}
componentDidMount() {
const fetchData = async () => {
//fetch api
this.setState({info: data,recommandation:data_recommandation_spliced })
}
fetchData();
}
componentWillReceiveProps(nextProps) {
console.log("RENDERING" + nextProps.movie_id)
const fetchData = async () => {
// fetch api
this.setState({id: nextProps.movie_id,info: data,recommandation:data_recommandation_spliced })
console.log("Rendered" + nextProps.movie_id)
}
fetchData();
}
render() {
return (
//css
{this.state.recommandation.map((movie) =>
<Link to={`/movie/${movie.id}`}>
<img src = {API_IMAGES + movie.poster_path} className="image-movie-genre"/>
</Link>
)}
)
}
}
export default DetailFilm;
Thanks for helping !
When adding JSX elements from an array, each one needs a unique key property so that React can keep track of necessary changes in the DOM. You need to add keys to your Link elements so that React will know to update them.
I found a solution which wasn't the one i was looking for at first.
I changed from using a Class to a function using useEffect avec id as param.
I am creating a live score website that will be returning all football live events. I am using jquery to send request then getting an object response that has fixures - this contains an array of all live events and finally result - this counts the number of events (21); I have trouble mapping this data so that I can display it on my app attached is the image of response
import React from 'react';
import jQuery from 'jquery';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
liveEvents: '',
loading: true
};
}
settingState = data => {
this.setState({
liveEvents: data.api
});
};
componentDidMount() {
var h = this;
var settings = {
async: true,
crossDomain: true,
url: 'https://api-football-v1.p.rapidapi.com/v2/fixtures/live',
method: 'GET',
headers: {
'x-rapidapi-host': 'api-football-v1.p.rapidapi.com',
'x-rapidapi-key': 'RAPIDAPI-KEY' //Private
}
};
jQuery.ajax(settings).done(function(response) {
h.setState({
loading: false
});
h.settingState(response);
});
}
render() {
var { liveEvents, loading } = this.state;
return (
<div className="livesheet">
{loading ? 'loading ' : console.log(liveEvents)}
</div>
);
}
}
export default Home;
Please refer to what is JSX.
You need to map your data in liveEvents to ReactElement / ReactElements array:
class Home extends React.Component {
...
render() {
const { liveEvents, loading } = this.state;
const { results, fixtures } = liveEvents;
// Example for rendering all fixture ids
const fixturesArray = fixtures.map(fixture => (
<div key={fixture.fixture_id}>{fixture.fixture_id}</div>
));
return (
<div className="livesheet">{loading ? 'loading' : fixturesArray}</div>
);
}
}
export default Home;
Also, you don't really need to use jQuery with react, read how to make AJAX calls and all relevant subject in the docs.
You can put your data in state, and show them in your app (in render) like this:
<div>{this.state.mydata.map
(m=><div>m.teamname</div>)}
</div>
update
Well if you have want to extract array as well, you should write another map in first map like this:
<div>
{this.state.mydata.map(mydata=>
<div>
{mydata.map(myarray=>
<div>{myarray.teamname}</div>
)mydata.id
</div>)}
</div>
Its something like this, you probbly need to check the syntax,if you use two nested map you can extract your array in your data as well
I hope you get the idea and works for you
I built a component which uses Axios get request and retrieves a list of email addresses.
I don't know what should I write inside render() so I will be able to see the emails list over my website.
This is my suggestion:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Link} from "react-router";
import axios from 'axios';
export class GetEmailsComponent extends Component {
state = {
emails: []
}
componentDidMount(){
//this.setState({emailList : undefined});
axios.get('./api/EmailAddresses')
.then(response => {
this.setState({emails: response.data});
console.log(response.data);
}).catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<button type = "button" onClick= {this.state.emails.map(email => <div>{email}</div>)}>GET ALL EMAILS</button>
</div>
);
}
}
When I check the Console I see an array of the desired emails.
I am looking for a suggestion of how to edit my code so it will render all this mails to the screen (After the button clicked).
Thanks is advance.
Inside your render method, you can map over this.state.emails and return an element for each email (At first the array will be empty, so maybe you can add a condition so you wouldn't render an empty div for no reason) Something like:
render() {
return (
<div>
{this.state.emails.map(email => <div>{email}</div>)}
</div>
);
}
As for componentDidMount - It's a lifecycle method of React. Anything you put there will run after the component mounts for the first time. If you want to trigger the call to Axios once the button is clicked, define a different function (like fetchEmails) and call it using this.fetchEmails.
You have used a componentDidMount life cycle in react to fetch the data. And you called that method via a button. Normally we are not calling life cycle methods like this. i think its better to read the react documentation doc for get an idea about life cycles.
You can declare a function and can call that function via a button. Please find below answer.
class App extends Component {
constructor(props) {
super(props);
this.state = {
emails: [],
showEmails:false,
};
}
componentDidMount () {
axios
.get("./api/EmailAddresses")
.then(response => {
this.setState({ emails: response.data });
console.log(response.data);
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
<button type="button" onClick={() => this.setState({showEmail:true})}>
Get all mails
</button>
{this.state.showEmail && this.state.emails.map(email => <div>{email}</div>)}
</div>
);
}
}
Change your code to something like below.
You need to get emails when button is clicked so you need have custom event handler function for that but not componentDidMount method. You cannot call componentDidMount method as event handler function.
Also when you render emails in loop you need to set unique key to top element inside loop. Key can be a index or unique id from data. Looks like you don’t have unique id from emails array so you can use index as key like below
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Link} from "react-router";
import axios from 'axios';
export class GetEmailsComponent extends Component {
state = {
emails: []
}
getEmails = () =>{
//this.setState({emailList : undefined});
axios.get('./api/EmailAddresses')
.then(response => {
this.setState({emails: response.data});
console.log(response.data);
}).catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<ul>
{this.state.emails.map((email, index)=> <li key={"Key-"+index}>{email}</li>)}
</ul>
<button type="button" onClick={()=> this.getEmails()}>Get all mails</button>
</div>
)
}
}
I am trying to write a thing that lets the user move through posts. So you look at a particular post, and then you can go to the previous or next post. I am trying to do this with react router. So say the user looks at posts/3, then by clicking NEXT he or she will be redirected to posts/4 and then see post no. 4.
However, it does not work yet. Clicking the buttons works fine, and it also does change the URL in the browser. However, I do not know how I can then fetch a new post (and populate my currentPost reducer anew), whenever the route changes.
What I have so far is this:
import React from 'react'
import {connect} from 'react-redux'
import {fetchPost} from '../actions/currentPost.js'
class PostView extends React.Component {
constructor(props) {
super(props);
this.setNextPost = this.setNextPost.bind(this);
this.setPreviousPost = this.setPreviousPost.bind(this);
}
componentDidMount() {
const {id} = this.props.match.params;
this.props.fetchPost(id);
console.log("HELLO");
}
setPreviousPost() {
var {id} = this.props.match.params;
id--;
this.props.history.push('/Posts/1');
}
setNextPost() {
var {id} = this.props.match.params;
id++;
this.props.history.push('/Posts/'+id);
}
render() {
return (
<div>
<h1>Here is a Post</h1>
<button onClick={this.setPreviousPost}>Previous</button>
<button onClick={this.setNextPost}>Next</button>
</div>
);
}
}
function mapStateToProps (state) {
return {
currentPost: state.currentPost
};
}
export default connect(mapStateToProps, {fetchPost})(PostView);
The lifecycle method you're looking for is componentWillReceiveProps
Here's more or less what it would look like:
class Component extends React.Component {
componentWillReceiveProps(nextProps) {
const currentId = this.props.id
const nextId = nextProps.id
if (currentId !== nextId) {
this.props.fetchPost(nextId)
}
}
}
from there, I think Redux/React will handle the rest for you.
I've been playing around with this for a while and am not sure if it's not possible or if I'm just missing something fundamental in ES6,React or MobX.
I want to have a mobx store in a separate file like below;
import { observable, action } from 'mobx';
export default class Store {
#observable data = [];
#action getAll(){
this.data = [{
itemTitle: 'Item One'
},{
itemTitle: 'Item Two'
}]
return this.data
}
#action pushItem(item){
this.data.push(item)
}
addAfter5Secs(){
setTimeout(() => {
console.log('Item Added')
this.pushItem({
itemTitle: 'Item Two'
})}, 5000)
}
constructor() {
console.log('Store Created')
this.getAll();
this.addAfter5Secs()
}
}
Then I want to import it in a view AND create an instance of that store at that time.
import React from "react";
import { List, Button } from 'semantic-ui-react';
import { observer, inject } from 'mobx-react';
import Store from '../Data/Store';
import DevTools from 'mobx-react-devtools';
const dataItems = new Store
#observer
export default class ScriptsHome extends React.Component {
render() {
const items = observer(dataItems)
return (
<List>
{items.data.map((reg, key) => {
return(
<List.Item key={key}>
<Button content={reg.itemTitle}/>
</List.Item>)
})}
<DevTools />
</List>
);
}
}
I do not want to pass it through a provider from the root component or have the store instantiated with the 'new' keyword in the export. I actually have dozens of stores so I only want them to be created as needed by views.
The above code will render the first 2 items, the 3rd item will be added to the store only after 5 seconds but the render is not fired on the component automatically so the item doesn't appear. When I force the render function to fire it will appear.
I am using a router to call this view so maybe that's also problematic?
I'm just learning Mobx, react, etc so any help is appreciated. All examples seem to use just one or two stores.
So after spending a bit of time with this it's turns out that it's not an issue at all with MobX.
The versions of react and react router seemed to be the problem.
Using react router dom now and it's fine.