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.
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 want to show the Logout button on the same row of the title but only when the user has made it to Home component.
In other words, I don't want to show the logout button at all times, especially when the user's at the login screen. I want it to show on the same row of the title only when they've logged in successfully and they're in Home
How would I achieve this? My head hurts from trying to make this work :(
Below's what I've tried so far, among other things.
import React, {Component} from 'react';
import classes from './Title.css';
import LogoutButton from '../../containers/LogoutButton/LogoutButton';
import Home from '../../components/Home/Home';
class Title extends Component {
constructor(props) {
super(props);
this.state = {
show: false,
showLogoutButton: true
};
}
showButton() {
this.setState({show: true});
if(this.state.show) {
return <LogoutButton/>;
} else {
return null;
}
}
render() {
return(
<div>
{ this.state.showLogoutButton ? this.showButton : null }
<h1 className={classes.Title}>Pick Ups</h1>
</div>
);
}
}
export default Title;
You can try something like below. You don't need to deal with function and modifying states.
You can simply do like below
import classes from './Title.css';
import LogoutButton from '../../containers/LogoutButton/LogoutButton';
import Home from '../../components/Home/Home';
class Title extends Component {
constructor(props) {
super(props);
this.state = {
showLogoutButton: this.props.authenticated
};
}
render() {
const { showLogoutButton } = this.state;
return(
<div className="row" style={{"display" :"flex"}}>
{ showLogoutButton && <LogoutButton/>}
<h1 className={classes.Title}>Pick Ups</h1>
</div>
);
}
}
export default Title;
Note: When you modify state using setState the state value will be updated only after render so you can't directly check immediately modifying the value.
Update: Apparently the bug is fixed. I never pushed a solution, so I'm still not sure what the problem/solution.
Essentially what's going on is that I have a child component that is being passed state from the main application component. I know that works fine, as I see the default value of the state showing up properly.
When the child mounts, it fires an ajax call to fetch some data, and then fires an action to update the state value accordingly (Other packages use this fetch call and it works fine as well). I can see all of this is working as expected by taking a look at the Redux chrome devtool. It shows the action being fired, and that the state has changed from the default value to the value it fetched.
The problem is that the page still shows that default value and not the new state value. So I'm wondering if there's an issue with calling that fetch request/state update and then expecting the component to properly update. Should I pass the state as a prop one level lower and have a component that only focuses on displaying that value? It's clear that everything is working as expected, the page is just not updating when the new state value is set.
Here's the code for child component that is not updating (had to modify for privacy purposes)
import { bindActionCreators, Component, connect, createElement, PropTypes } from 'somePackage';
import { getStatus } from 'somedirectory';
class ChildComponent extends Component {
constructor(props) {
super(props);
this.state = {
irrelevantState: false,
};
}
componentDidMount() {
this.fetchMyData();
}
fetchMyData() {
const {
boundNavActions,
} = this.props;
boundNavActions.getStatus();
}
render() {
const {
**stateImLookingAt**,
irrelevantString,
irrelevantString,
} = this.props;
return (
<div>
<div styleName="irrelevantString">
<div styleName="irrelevantString">
<a
href={ irrelevantString }
aria-label={ irrelevantString }
>
<div
spriteSheetType="irrelevantString"
name={ irrelevantString }
/>
//Would making this it's own component help?
<div styleName="thisDoesntUpdate">
{ **stateImLookingAt** }
</div>
//Would making this it's own component help?
</a>
</div>
</div>
</div>
);
}
}
ChildComponent.propTypes = {
boundNavActions: PropTypes.object,
cartCount: PropTypes.number,
};
const mapDispatchToProps = dispatch => ({
boundNavActions: bindActionCreators({
getStatus,
}, dispatch),
});
export default connect(null, mapDispatchToProps)(ChildComponent);
There's not a lot going on pertaining to this state in the parent but here's a snippet
import { connect, createElement, PropTypes } from 'somedirectory';
import ChildComponent from 'ChildComponentPackage';
import './app.css';
const AppContainer = (props) => {
const {
**stateImLookingAt**,
} = props;
return (
<div styleName="root">
<ChildComponent
**stateImLookingAt**={ **stateImLookingAt** }
/>
</div>
);
};
const mapStateToProps = state => ({
**stateImLookingAt**: state.moo.cow.**stateImLookingAt**,
});
AppContainer.propTypes = {
**stateImLookingAt**: PropTypes.number.isRequired,
};
export default connect(mapStateToProps)(AppContainer);
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;
I've gone through many of the Redux and ReactJS tuts. I understand setting actions => action creators => dispatch => store => render view (uni-directional flow) with more data substantial events. My problem is dealing with very simple events that change state. I know not all state always needs to be handled in Redux, and that local state events (set on React components) is an acceptable practice. However, technically Redux can handle all state events and this is what I am trying to do.
Here is the issue. I have a React component that renders a Button. This Button has an onClick event that fires a handleClick function. I set the state of the Button via the constructor method to isActive: false. When handleClick fires, setState sets isActive: true. The handleClick method also runs two if statements that, when either evaluate to true, run a function that either changes the background color of the body or the color of paragraph text. Clicking the same button again sets state back to false and will change back the body color or text color to the original value. This Button component is created twice within a separate component, Header. So long story short, I've got two buttons. One changes body color, the other changes p tag color after a click event.
Here's the code for the Button component:
import React, {Component} from 'react';
import {dimLights, invertColor} from '../../../actions/headerButtons';
import { connect } from 'react-redux';
import { Actions } from '../../../reducers/reducer';
const headerButtonWrapper = 'headerButton';
const headerButtonContext = 'hb--ctrls ';
const dimmedLight = '#333333';
const invertedTextColor = '#FFFFFF';
export default class Button extends Component {
constructor (props) {
super(props)
this.state = {
isActive: false
};
}
handleClick (e) {
e.preventDefault();
let active = !this.state.isActive;
this.setState({ isActive: active });
if(this.props.label === "Dim The Lights"){
dimLights('body', dimmedLight);
}
if(this.props.label === "Invert Text Color"){
invertColor('p', invertedTextColor)
}
}
render() {
let hbClasses = headerButtonContext + this.state.isActive;
return (
<div className={headerButtonWrapper}>
<button className={hbClasses} onClick={this.handleClick.bind(this)}>{this.props.label}</button>
</div>
);
}
}
Here's the code for the imported functions that handle changing the colors:
export function dimLights(elem, color) {
let property = document.querySelector(elem);
if (property.className !== 'lightsOn') {
property.style.backgroundColor = color;
property.className = 'lightsOn'
}
else {
property.style.backgroundColor = '#FFFFFF';
property.className = 'lightsOff';
}
}
export function invertColor(elem, textColor) {
let property = document.querySelectorAll(elem), i;
for (i = 0; i < property.length; ++i) {
if (property[i].className !== 'inverted') {
property[i].style.color = textColor;
property[i].className = 'inverted'
} else {
property[i].style.color = '#3B3B3B';
property[i].className = 'notInverted';
}
}
}
Here's the code for the reducers:
import * as types from '../constants/ActionTypes';
const initialState = {
isActive: false
};
export default function Actions(state = initialState, action) {
switch (action.type) {
case types.TOGGLE_LIGHTS:
return [
...state,
{
isActive: true
}
]
default:
return state
}
}
Here's the code for the actions:
import EasyActions from 'redux-easy-actions';
export default EasyActions({
TOGGLE_LIGHTS(type, isActive){
return {type, isActive}
}
})
If it helps, here's the Header component that renders two Button components:
import React, {Component} from 'react';
import Button from './components/Button';
const dimmer = 'titleBar--button__dimmer';
const invert = 'titleBar--button__invert';
export default class Header extends Component {
render() {
return (
<div id="titleBar">
<div className="titleBar--contents">
<div className="titleBar--title">Organizer</div>
<Button className={dimmer} label="Dim The Lights" />
<Button className={invert} label="Invert Text Color" />
</div>
</div>
);
}
}
Finally, here's the code containing the store and connection to Redux (NOTE: Layout contains three main components Header, Hero, and Info. The Buttons are created only within the Header component)
import React, { Component } from 'react';
import { combineReducers } from 'redux';
import { createStore } from 'redux'
import { Provider } from 'react-redux';
import Layout from '../components/Layout';
import * as reducers from '../reducers/reducer';
const reducer = combineReducers(reducers);
const store = createStore(reducer);
// This is dispatch was just a test to try and figure this problem out
store.dispatch({
type: 'TOGGLE_LIGHTS',
isActive: true
})
console.log(store.getState())
export default class Organizer extends Component {
render() {
return (
<Provider store={store}>
<div>
<Layout />
</div>
</Provider>
);
}
}
What I am looking to do is remove the state logic from the local React component and into Redux. I feel like the functions I have imported need to act as dispatchers. I also feel like I am setting up my initial actions incorrectly. This is such an incredibly simple event that finding an answer anywhere online is difficult. Anyone have any thoughts on what I can do to fix this?
You're almost there. It looks like you've left out the code for Layout component, which I assume is the component that's rendering your Button. The critical piece here is going to be your container, which is the component that's wrapped with Redux's connect to link it to the store. Docs for this. More details here.
What you did:
// components/Button.js - pseudocode
import {dimLights, invertColor} from '../../../actions/headerButtons';
handleClick() {
dimLights();
}
What Redux wants you to do instead:
// containers/App.js - pseudocode
import {dimLights, invertColor} from '../../../actions/headerButtons';
class App extends Component {
render() {
// Pass in your button state from the store, as well as
// your connected/dispatch-ified actions.
return (
<Button
state={this.props.buttonState}
onClick={this.props.buttonState ? dimLights : invertColor}
/>
);
}
}
function mapStateToProps(state, ownProps) {
return {
buttonState: state.buttonState
};
}
export default connect(mapStateToProps, {
// Your action functions passed in here get "dispatch-ified"
// and will dispatch Redux actions instead of returning
// { type, payload }-style objects.
dimLights, invertColor
})(App);
Hope that helps! Redux has a lot of boilerplate for simple stuff like this, however, because most of the pieces can be expressed as pure functions, you gain a lot in unit testing flexibility, and get to use the devtools debugger.