How to get and pass object(JSON API) in React/Redux? - json

i have a problem with getting and passing JSON object (info) from Store in React "Container" to his child component (InfoPage) via props..
Also i had Action's and Reducer's methods, all of there works without mistakes.
Could somebody help me with this.
This is my code from "Container".
thanks.
function mapStateToProps(state) {
return {
user: state.auth.user,
info: state.info,
ui: state.ui
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(Object.assign({}, uiActions,
authActions, infoActions), dispatch);
}
class Info extends Component {
static propTypes = {
user: PropTypes.objectOf(PropTypes.any).isRequired,
ui: PropTypes.objectOf(PropTypes.any).isRequired,
info: PropTypes.objectOf(PropTypes.any).isRequired,
switchProfileMenu: PropTypes.func.isRequired,
logOut: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.handleClickOption = this.handleClickOption.bind(this);
}
handleClickOption() {
return this.props;
}
render() {
const { user, logOut, info, ui: { showProfileMenu },
switchProfileMenu } = this.props;
return (
<div className={b()}>
<Header
user={user}
logOut={logOut}
switchMenu={switchProfileMenu}
showMenu={showProfileMenu}
/>
<div className="container">
<div className="content">
<div>
<InfoPage data={info} />
sadasd
</div>
</div>
</div>
<Footer />
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

I gave a look to your code and there are some missing parts.
First, the function getInfo looks to be a thunk (redux-thunk, which allows you to execute asynchronous operations). You have to import it inside your index and initialize it is, not an array
import thunk from 'redux-thunk'
// ...
createStore(rooReducer, applyMiddlewares(thunk))
You can define you mapDispatchToProps in a simple way, for now:
import getInfo from './actions'
// ....
function mapDispatchToProps (dispatch) {
return {
dispatchGetInfo(id) {
dispatch(getInfo(id))
}
}
}
Inside your component Info, implement like this:
componentDidMount () {
const { dispatchGetInfo, params: { id } } = this.props
// I PRESUME your `id` is inside the params of react router, feel free to change if not
dispatchGetInfo(id) // => this should call your thunk which will call your consecutive dispatcher
}
EDIT
Your index file should contain this modified line
<Route name="app:info" path="information/:id" component={Info} />

Related

Wrapping a React Route render component in a HOC

I wrapped the Profile component in a HOC that is supposed to redirect the user to certain page once he logs out while in this route, like so :
<Route
path="/profile/:username"
render={props=> withAuth(<Profile currentUser={currentUser} {...props} />)}
/>
I get this Error:
Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare, WrappedComponent, displayName}). If you meant to render a collection of children, use an array instead.
this is My HOC:
import React, { Component } from "react";
import { connect } from "react-redux";
export default function withAuth(ComponentToBeRendered) {
class Authenticate extends Component {
componentDidMount() {
if (this.props.isAuthenticated === false) {
this.props.history.push("/signin");
}
}
componentWillUpdate(nextProps) {
if (nextProps.isAuthenticated === false) {
this.props.history.push("/signin");
}
}
render() {
return ComponentToBeRendered ;
}
}
function mapStateToProps(state) {
return {
isAuthenticated: state.currentUser.isAuthenticated
}
}
return connect(mapStateToProps)(Authenticate);
}
This is My Profile Component:
import MessageList from "../containers/MessageList";
import UserAside from "./UserAside";
import { connect } from 'react-redux';
import { selectTheUser } from '../store/selectors'
const Profile = props => {
return (
<div className="row">
<MessageList {...props} />
<UserAside
{...props} />
</div>
)
}
const mapStateToProps = (state, props) => ({
userToVisit: selectTheUser(props.match.params.username)(state)
})
export default connect(mapStateToProps)(Profile);
In your HOC
render() {
return <ComponentToBeRendered {...this.props}/> ;
}
In your Router
const ProfileWithAuth = withAuth(Profile)
<Route
path="/profile/:username"
render={props=> (<ProfileWithAuth currentUser={currentUser} {...props}/>)}
/>

How to create dynamic routes with react-router-dom?

I learn react and know, how to create static routes, but can't figure out with dynamic ones. Maybe someone can explain, I'll be very grateful. Let there be two components, one for rendering routes, and another as a template of a route. Maybe something wrong in the code, but hope You understand..
Here is the component to render routes:
import React, { Component } from 'react';
import axios from 'axios';
import Hero from './Hero';
class Heroes extends Component {
constructor(props) {
super(props);
this.state = {
heroes: [],
loading: true,
error: false,
};
}
componentDidMount() {
axios.get('http://localhost:5555/heroes')
.then(res => {
const heroes = res.data;
this.setState({ heroes, loading: false });
})
.catch(err => { // log request error and prevent access to undefined state
this.setState({ loading: false, error: true });
console.error(err);
})
}
render() {
if (this.state.loading) {
return (
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.heroes) {
return (
<div>
<p> An error occured </p>
</div>
)
}
return (
<div>
<BrowserRouter>
//what should be here?
</BrowserRouter>
</div>
);
}
}
export default Heroes;
The requested JSON looks like this:
const heroes = [
{
"id": 0,
"name": "John Smith",
"speciality": "Wizard"
},
{
"id": 1,
"name": "Crag Hack",
"speciality": "Viking"
},
{
"id": 2,
"name": "Silvio",
"speciality": "Warrior"
}
];
The route component (maybe there should be props, but how to do it in the right way):
import React, { Component } from 'react';
class Hero extends Component {
render() {
return (
<div>
//what should be here?
</div>
);
}
}
export default Hero;
I need something like this in browser, and every route url should be differentiaie by it's id (heroes/1, heroes/2 ...):
John Smith
Crag Hack
Silvio
Each of them:
John Smith.
Wizard.
and so on...
Many thanks for any help!)
Use Link to dynamically generate a list of routes.
Use : to indicate url params, :id in the case
Use the match object passed as props to the rendered route component to access the url params. this.props.match.params.id
<BrowserRouter>
/* Links */
{heroes.map(hero => (<Link to={'heroes/' + hero.id} />)}
/* Component */
<Route path="heroes/:id" component={Hero} />
</BrowserRouter>
class Hero extends Component {
render() {
return (
<div>
{this.props.match.params.id}
</div>
);
}
}
Update so this works for React Router v6:
React Router v6 brought some changes to the general syntax:
Before: <Route path="heroes/:id" component={Hero} />
Now: <Route path="heroes/:id" element={<Hero />} />
You can't access params like with this.props.match anymore:
Before: this.props.match.params.id
Now: import {useParams} from "react-router-dom";
const {id} = useParams();
You can now just use id as any other variable.
To do this you simply add a colon before the url part that should be dynamic. Example:
<BrowserRouter>
{/* Dynamic Component */}
<Route path="heroes/:id" component={Hero} />
</BrowserRouter>
Also you can use the useParams hook from react-router-dom to get the dynamic value for use in the page created dynamically. Example:
import { useParams } from "react-router-dom";
const Hero = () => {
const params = useParams();
// params.id => dynamic value defined as id in route
// e.g '/heroes/1234' -> params.id equals 1234
return (...)
}

Understanding React.JS JSON feed and how to parse

I've got the following code which is looping through an JSON file from an API and loops through some posts.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
//https://alligator.io/react/axios-react/
import axios from 'axios';
export default class PostList extends React.Component {
state = {
posts: []
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const posts = res.data;
this.setState({ posts });
})
}
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">
Pulls in post slugs from Domain
</p>
<ul>
{ this.state.posts.map(post => <li>{post.name} - {post.username} </li>)}
</ul>
</div>
)
}
}
This works fine, and gets the information which was needed.
Now, in my test JSON file, the format is as follows:
https://jsonplaceholder.typicode.com/users
But in my actual JSON file from WordPress Rest API, we have another item, named core_layout:
JSON image
My issue is, trying to use the same code such as {post.name}does not get the information needed such as core_layout->image->name.
Is there an easy way around this?
Thanks all!
EDIT:
Tried the answers below, but still no luck, get the error TypeError: Cannot read property 'map' of undefined:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
//https://alligator.io/react/axios-react/
import axios from 'axios';
export default class PostList extends React.Component {
state = {
posts: [],
coreLayout: {}
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
// const posts = res.data;
//this.setState({ posts });
const { posts, core_layout: coreLayout } = res.data;
this.setState({ posts, coreLayout });
})
}
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">
Pulls in post slugs from domain
</p>
<ul>
{ this.state.posts.map(post => <li>{post.name} - {post.core_layout.image.name}</li>)}
</ul>
</div>
)
}
}
EDIT 2:
Tried the below: This gets the title, but again, not the actual corelayout I need.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
movies: []
}
}
componentDidMount() {
let dataURL = "http://zinsseruk.com/wp-json/wp/v2/posts?per_page=1";
fetch(dataURL)
.then(res => res.json())
.then(res => {
this.setState({
movies: res
})
})
}
render() {
let movies = this.state.movies.map((movie, index) => {
return <div key={index}>
<p><strong>Title:</strong> {movie.title.rendered}</p>
<p><strong>Title:</strong> {movie.core_layout.acf_fc_layout}</p>
</div>
});
return (
<div>
<h2>Star Wars Movies</h2>
{movies}
</div>
)
}
}
export default App;
Replace const posts = res.data; with const posts = res.data.core_layout;. Then you'll get an array similar to what you have in your test file.
I think you need to understand the JSON structure you receive from the API. Where is located core_layout property? Inside each post property as a children?
So in the posts loop you can use post.core_layout.image.name for image name, for example (and so on with other properties).
If core_property is at the root of the data you receive, you can load it inside your state like so:
state = {
posts: [],
coreLayout: {}
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
// This is equivalent of doing
// const posts = res.data.posts
// const coreLayout = res.data.core_layout
const { posts, core_layout: coreLayout } = res.data;
this.setState({ posts, coreLayout });
})
}
Then use it in your code by using local component state:
render() {
...
// For example image name:
console.log('image name', this.state.coreLayout.image.name)
...
}

How to create a link that goes back in react-router-dom v4

I know I can access history.goBack() to go back in the router history.
However, I'd like to create a <Link /> tag that has this functionality and relies on the to property (href) to navigate back rather than an onClick.
Is this possible?
I may have a solution to your problem using the context api.
But I strongly believe that it would be easier to use history.goBack().
First you'll need to wrap the App component inside a router:
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root'),
);
Then in your your App/index.js file you'll need to listen to the location change event and set your state accordingly:
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import { withRouter } from 'react-router'
class App extends Component {
state = { prevLocation: '' };
// Use the context api to retrieve the value in your Link
getChildContext = () => (
{
prevLocation: this.state.prevLocation,
}
);
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
this.setState({ prevLocation: this.props.location.pathname });
}
}
render() {
return (
<div>
<Switch>
// ...
</Switch>
</div>
);
}
}
App.childContextTypes = {
prevLocation: PropTypes.string,
};
export default withRouter(App);
Then in can create a GoBack component and use the context API to retrieve the value the previous path.
import React from 'react';
class GoBack extends React.Component {
render() {
return <Link to={this.context.prevLocation}>click</Link);
}
}
GoBack.contextTypes = {
prevLocation: PropTypes.string,
};

Cannot read property 'toObject' of undefined when trying to call a func property

This is my component
class MyComponent extends Component {
render () {
const { action } = this.props;
action();
return (<div>Done!</div>);
}
MyComponent.propTypes = {
action: PropTypes.func.isRequired
}
And here is the relevant code of a container:
doSomething () {
...
}
render() {
return (
<MyComponent
action={doSomething}
/>
)
}
When I bring up this code in a browser, I got this error message:
Uncaught TypeError: Cannot read property 'toObject' of undefined
Business logic should live in container so I do not want to copy and paste the code of action into MyComponent.
So my question is: how can I call a function passed in via properties directly in a render method?
I think, issue is in this place:
doSomething () {
...
}
render() {
return (
<MyComponent
action={doSomething} //here
/>
)
}
It should be:
doSomething () {
...
}
render() {
return (
<MyComponent
action={this.doSomething}
/>
)
}
You need to use this.doSomething instead of doSomething.
Check the working example:
class App extends React.Component{
constructor(){
super();
}
doSomething(){
console.log('called');
}
render(){
return(
<div>
Hello
<Child action={this.doSomething}/>
</div>
)
}
}
var Child = (props) => {
const {action} = props
action();
return(
<div>Child</div>
)
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>