React Axios better way to get single post from WordPress REST - wordpress

I'm using the following to fetch a single post from the WordPress REST API.
import React, { Component } from 'react';
import axios from 'axios';
class Post extends Component {
constructor() {
super();
this.state = {
post: [],
};
}
componentDidMount() {
axios.get('http://example.com/wp-json/wp/v2/posts?slug=some-post')
.then(response => {
this.setState({ post: response.data });
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<div>
{this.state.post.map(single => {
return(
<div>
<h1>{single.title.rendered}</h1>
<p>{single.content.rendered}</p>
</div>
);
})}
</div>
);
}
}
export default Post;
Is there a better/more straightforward way to render the post without mapping an array?

If api returns array then you can take only first element, f.e.:
this.setState({ post: response.data[0] });
Of course you should use then some conditional rendering (map works with empty array):
if(!this.state.post) return <Loading />

Related

React Redux useEffect not loading data on initial page render

I'm trying to render a quiz object from an api and am using useEffect to do so, but it doesn't seem to be working.
Here is my Quiz component:
export function Quiz(props) {
const {
quiz,
fetchQuiz,
} = props;
useEffect(() => {
fetchQuiz();
}, [])
return (
<div id="wrapper">
{ quiz.question }
</div>
)
}
const mapStateToProps = state => {
return {
quiz: state.quiz,
}
}
export default connect(mapStateToProps, {
fetchQuiz,
})(Quiz)
my action creator:
export function fetchQuiz() {
return function (dispatch) {
axios. get('http://localhost:9000/api/quiz/next')
.then(res => {
dispatch({ type: SET_QUIZ_INTO_STATE, payload: res.data })
})
.catch(err => console.error(err))
}
}
my reducer:
const initialQuizState = null
function quiz(state = initialQuizState, action) {
switch(action.type) {
case SET_QUIZ_INTO_STATE:
return action.payload
default:
return state
}
}
So when I console.log "quiz" in my Quiz component, I get the object that I am expecting and it's all good. But when I try and call "quiz.question" I get the error: Cannot read properties of null (reading question). Which leads me to believe useEffect is not loading the data from the api correctly. Am I doing something else wrong?
When accessing an object from an API response in the DOM, make sure you have a conditional to check for completion like so:
return (
<div id="wrapper">
{ quiz ? quiz.question : 'No Quiz Here' }
</div>
)
}

WordPress with React: Saving and Using Data Collected with fetch

I am trying to do a hopefully basic thing with react, which is access an endpoint created by my locally installed WordPress website so that I can use that data and render it in a way I like.
I am trying to set the state to the data but alsough it can be printed to the console in the componentWillMount() function, the state posts remains empty. I can console.log the data from that function but I cannot set the state and then use it in the render function. My code is below:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
componentWillMount() {
const theUrl = "http://localhost:8888/test-site/wp-json/wp/v2/posts";
fetch(theUrl)
.then(response => response.json())
.then(response =>
this.setState({
posts: response
})
)
}
render() {
console.log('render posts: ' + this.state.posts);
return (
<div>
<h1>React Widget</h1>
<p>posts:</p>
</div>
);
}
}
Widget.propTypes = {
wpObject: PropTypes.object
};
Console:
JQMIGRATE: Migrate is installed, version 1.4.1
react-dom.development.js:21258 Download the React DevTools for a better development experience: https://.me/react-devtools
react-dom.development.js:21258 Download the React DevTools for a better development experience: https://.me/react-devtools
Widget.jsx:28 render posts:
jquery.loader.js:2 running on http://localhost:8888/test-site/
Widget.jsx:28 render posts: [object Object],[object Object],[object Object],[object Object]
the second to last line in the console has a collapse arrow next to it and I can see that those are in fact the posts with all of their correct information. Why can I not set the state to the data being returned by fetch()?
React has a special function setState() to set a components state (for class components). So instead of a direct assignment
this.state = {
value: 'foo2',
posts: data.value,
};
use
this.setState({
value: 'foo2',
posts: data.value,
})
.
This results in the following code. Also, your fetch belongs into the componentDidMount() function (I missed that in my first draft).
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
componentDidMount() {
fetch("http://localhost:8888/test-site/wp-json/wp/v2/posts")
.then(data => data.json())
.then(data => this.setState({posts: data.value}))
}
render() {
return (
<div>
<p>value: {this.state.posts}</p>
</div>
);
}
}
For this special example, you might also want to use functional components with a useState() and a useEffect hook:
export default function Widget() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch("http://localhost:8888/test-site/wp-json/wp/v2/posts")
.then(data => data.json())
.then(data => setPosts(data.value))
});
return (
<div>
<p>value: {posts}</p>
</div>
);
}

Next.js + Redux server side rendering: Has data, but doesn't render on server side

I'm trying to add redux integration to my Next.js app, but I can't get serverside rendering working the way it should. I based my implementation off the official nextjs redux example.
In the end, when the page comes back from the server, the data is present as JSON data in the output, but the actual rendering based on this data did not happen. The weird thing is that before I used redux, the content DID render the way it should.
Naturally, I'm also getting React's checksum warning, indicating that the markup on the server is different.
I have no idea how to make this work properly on the server side. Is there something that I'm missing?
Here's the HTML generated by Next.js:
<h1 data-reactid="3">Test page</h1>
</div></div></div><div id="__next-error"></div></div><div><script>
__NEXT_DATA__ = {"props":{"isServer":true,"store":{},
"initialState":{"authors":{"loading":false,"items":{"4nRpnr66B2CcQ4wsY04CIQ":… }
,"initialProps":{}},"pathname":"/test","query":{},"buildId":1504364251326,"buildStats":null,"assetPrefix":"","nextExport":false,"err":null,"chunks":[]}
module={}
__NEXT_LOADED_PAGES__ = []
__NEXT_LOADED_CHUNKS__ = []
__NEXT_REGISTER_PAGE = function (route, fn) {
__NEXT_LOADED_PAGES__.push({ route: route, fn: fn })
}
__NEXT_REGISTER_CHUNK = function (chunkName, fn) {
__NEXT_LOADED_CHUNKS__.push({ chunkName: chunkName, fn: fn })
}
</script><script async="" id="__NEXT_PAGE__/test" type="text/javascript" src="/_next/1504364251326/page/test"></script><script async="" id="__NEXT_PAGE__/_error" type="text/javascript" src="/_next/1504364251326/page/_error/index.js"></script><div></div><script type="text/javascript" src="/_next/1504364251326/manifest.js"></script><script type="text/javascript" src="/_next/1504364251326/commons.js"></script><script type="text/javascript" src="/_next/1504364251326/main.js"></script></div></body></html>
AS you can see, the initialState value is populated, it contains all the required data, but the DOM still shows empty!.
If I render the dom on the client side, the js picks up the initial content and rerenders the page with the loaded content in place.
Here's my test page JS file:
import React from 'react'
import map from 'lodash.map';
import { initStore } from '../lib/store';
import * as actions from '../lib/actions';
import withRedux from 'next-redux-wrapper';
class IndexPage extends React.PureComponent {
static getInitialProps = ({ store, req }) => Promise.all([
store.dispatch(actions.fetchAll)
]).then( () => ({}) )
render() {
const latestPlants = this.props.plants.latest || [];
return (
<div>
<h1>Test page</h1>
{ map(this.props.plants.items, p => (
<div>{p.fields.name}</div>
))}
</div>
)
}
}
export default withRedux(initStore, data => data, null)(IndexPage)
For whatever it's worth, here's the action that I call above:
export const fetchAll = dispatch => {
dispatch({
type: LOADING_ALL
})
return axios.get('/api/frontpage')
.then( response => {
const data = response.data
dispatch({
type: RESET_AUTHORS,
payload: data.authors
})
dispatch({
type: RESET_PLANTS,
payload: data.plants
})
dispatch({
type: RESET_POSTS,
payload: data.posts
})
});
}
Any help with this would be greatly appreciated, I'm at a loss on how to make this work as expected. Anyone have any leads? Please also comment if there's something I can clarify.
I recommend to split the code in different parts. First, I'll create a store, with something like this:
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import reducer from './reducers'
export const initStore = (initialState = {}) => {
return createStore(reducer, initialState, applyMiddleware(thunkMiddleware))
}
Then I'll create the store with the types to handle:
const initialState = {
authors: null,
plants: null,
posts: null
}
export default (state = initialState, action) => {
switch (action.type) {
case 'RESET':
return Object.assign({}, state, {
authors: action.authors,
plants: action.plants,
posts: action.posts
})
default:
return state
}
}
In the actions I'll have something like this:
export const fetchAll = dispatch => {
return axios.get('/api/frontpage')
.then( response => {
const data = response.data
dispatch({
type: 'RESET',
authors: data.authors,
plants: data.plants,
posts: data.posts
})
});
}
The index will be something like this:
import React from 'react'
import { initStore } from '../store'
import withRedux from 'next-redux-wrapper'
import Main from '../components'
class Example extends React.Component {
render() {
return (
<div>
<Main />
</div>
)
}
}
export default withRedux(initStore, null)(Example)
And the component Main:
import React, {Component} from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { fetchAll } from '../../actions'
class Data extends Component {
componentWillMount() {
this.props.fetchAll()
}
render() {
const { state } = this.props
return (
<div>
<h1>Test page</h1>
{ map(state.plants.items, p => (
<div>{p.fields.name}</div>
))}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
state
}
}
const mapDistpatchToProps = dispatch => {
return {
fetchAll: bindActionCreators(fetchAll, dispatch)
}
}
export default connect(mapStateToProps, mapDistpatchToProps)(Data)
Make the changes for what you need.
You can check some full examples here:
Form handler
Server Auth

Connecting React Component with Redux Store

Very basic simple GET example for react-redux
I have a "MockAPI" which simulates a GET request to an API like so:
const dashboards = [
{
"Id":1,
"title":"Overview"
},
{
"Id":2,
"title":"Overview"
},
{
"Id":3,
"title":"Overview"
},
{
"Id":4,
"title":"Overview"
}
];
class DashboardApi {
static getAllDashboards() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Object.assign([], dashboards));
}, delay);
});
}
}
I am trying to develop in a react-redux flow of dispatching an action via a button click and then updating the component via the redux store.
Here is my component code:
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import * as dashboardActions from '../../actions/dashboardActions';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.loadDashboards = this.loadDashboards.bind(this);
}
loadDashboards() {
this.props.dispatch(dashboardActions.loadDashboards());
}
dashboardItem(dashboard, index) {
return <p key={index}>{dashboard.title}</p>;
}
render() {
return (
<div>
<h1>
Hello World!
<button onClick={this.loadDashboards}>load</button>
</h1>
{this.props.dashboards.map(this.dashboardItem)}
</div>
);
}
}
HomePage.propTypes = {
dashboards: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
return {
dashboards: state.dashboards
};
}
export default connect(mapStateToProps)(HomePage);
And here is my dashboardActions.js:
import * as types from './actionTypes';
import dashboardApi from '../mockApi/mockDashboardApi';
export function loadDashboardsSuccess(dashboards) {
return { type: types.LOAD_DASHBOARDS_SUCCESS, dashboards };
}
export function loadDashboards() {
return dispatch => {
return dashboardApi
.getAllDashboards()
.then(dashboards => {
dispatch(loadDashboardsSuccess(dashboards));
});
};
}
And here is my reducer:
import initialState from './initialState';
import * as types from '../actions/actionTypes';
export default function dashboardReducer(state = initialState.dashboards, action) {
switch(action.types) {
case types.LOAD_DASHBOARDS_SUCCESS:
return action.dashboards;
default:
return state;
}
}
I am trying to get the onClick to load in the dashboards array and to render as <p> tags simply displaying the title value. Unfortunately it is not happening.
I see that the LOAD_DASHBOARDS_SUCCESS action is getting loaded, but I see that the dashboards property in the store is still an empty array instead of showing the returned data...
What am I missing here?
You've got a typo in your reducer. switch(action.types) should be switch(action.type) with no 's'

React props using Meteor Apollo

I am playing with the Meteor Apollo demo repo.
I am having difficulty passing variables down to children with React. I am getting an error
imports/ui/Container.jsx:10:6: Unexpected token (10:6)
The below code is the Container.jsx component:
import React from 'react';
import { Accounts } from 'meteor/std:accounts-ui';
class Container extends React.Component {
render() {
let userId = this.props.userId;
let currentUser = this.props.currentUser;
}
return (
<Accounts.ui.LoginForm />
{ userId ? (
<div>
<pre>{JSON.stringify(currentUser, null, 2)}</pre>
<button onClick={() => currentUser.refetch()}>Refetch!</button>
</div>
) : 'Please log in!' }
);
}
}
It is passed props via the Meteor Apollo data system (I have omitted some imports at the top):
const App = ({ userId, currentUser }) => {
return (
<div>
<Sidebar />
<Header />
<Container userId={userId} currentUser={currentUser} />
</div>
)
}
// This container brings in Apollo GraphQL data
const AppWithData = connect({
mapQueriesToProps({ ownProps }) {
if (ownProps.userId) {
return {
currentUser: {
query: `
query getUserData ($id: String!) {
user(id: $id) {
emails {
address
verified
}
randomString
}
}
`,
variables: {
id: ownProps.userId,
},
},
};
}
},
})(App);
// This container brings in Tracker-enabled Meteor data
const AppWithUserId = createContainer(() => {
return {
userId: Meteor.userId(),
};
}, AppWithData);
export default AppWithUserId;
I would really appreciate some pointers.
I believe the error is that you accidentally ended the render function before the return statement.
render() { // <- here it starts
let userId = this.props.userId;
let currentUser = this.props.currentUser;
} // <- here it ends
Another error is that your return statement doesn't return a single DOM element, but two of them: an Accounts.ui.LoginForm and a div. The return function should only return one element. Just put the entire thing into a single <div>.

Resources