Rerender another component when the state is changed - reactjs

I want to rerender my another component when the state is changed. I've conditional components. One of them is related to input in my main component. It is rendered in the first place but I cant rerender it when I change the input value (also state value). The component is SearchGifList
import React from 'react';
import logo from './logo.svg';
import './App.css';
import TrendingGifList from './TrendingGifList';
import SearchGifList from './SearchGifList';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
search: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
event.preventDefault();
this.setState({
search : true,
})
}
getTrendingGifs = () => {
this.setState({
search : false,
value: ''
})
}
getComponent = () => {
if(this.state.search) {
return <SearchGifList value={this.state.value} />
}
else {
return <TrendingGifList />
}
}
render() {
return (
<>
<div>
<a onClick={this.getTrendingGifs}>Logo</a>
<form onSubmit={this.handleSubmit}>
<input className="input-search" type="text" value={this.state.value} onChange={this.handleChange} />
<button type="submit" >Search</button>
</form>
</div>
<div>
{this.getComponent()}
</div>
</>
);
}
}
export default App;
SearchGifList Component code:
import React, { Component } from 'react';
import Masonry from 'react-masonry-css';
import {API_KEY, API_URL_search} from './constants'
class SearchGifList extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
breakpointColumnsObj : {
default: 4,
1100: 3,
700: 2,
500: 1
},
offset: 0,
limit: 20,
api_key: API_KEY,
total_count: 0,
value: this.props.value
};
}
searchGifs = () => {
fetch( API_URL_search +
"?q=" + this.state.value +
"&api_key=" + this.state.api_key +
"&limit=" + this.state.limit +
"&offset=" + this.state.offset)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: (this.state.items).concat(result.data),
total_count: result.pagination.total_count,
offset : this.state.offset + 20
});
},
(error) => {
this.setState({
isLoaded: true,
error : 'Somethings went wrong to search gifs.'
});
}
)
}
componentDidMount() {
this.searchGifs();
}
loadMore = () => {
if(this.state.offset < this.state.total_count){
this.setState({
offset : this.state.offset + 20
});
}
this.searchGifs();
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<>
<Masonry
breakpointCols={this.state.breakpointColumnsObj}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column"
>
{items.map(item => (
<div className="gif-container" >
<img className="gif-preview" height={item.images.fixed_width.height} src={item.images.fixed_width.webp} alt="giphy baby" />
</div>
))}
</Masonry>
<button onClick={this.loadMore}>Load More</button>
</>
);
}
}
}
export default SearchGifList;

It should render content after changing state. Here is an similar example I have created for you now. https://codesandbox.io/embed/example-ys041
Please check it I think that will help you a lot.
If still you are not getting any idea then send me the your example via codesandbox I will check it for you.
Thanks

I solved the problem componentDidUpdate() method:
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
setTimeout(() => {
this.setState({
items: [],
offset: 0
}, function(){
this.searchGifs();
});
}, 10)
}
}
Thank you for all answers

Related

How to make an axios POST request in React?

So, some context: Users submit a dog name via a text input, and this is controlled by the 'Dogue.jsx' component:
import React from 'react';
class Dogue extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.id,
nameInput: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
nameInput: e.target.value,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.inputFunction(this.state.nameInput);
}
render() {
console.log(this.props.id);
return (
<div className="dogue-container">
<img className="img" src={this.props.dogList} />
<br />
<form onSubmit={this.handleSubmit} className="form">
<input
onChange={this.handleChange}
className="input"
type="text"
placeholder="Enter dog name"
/>
<br />
<button className="button">Submit</button>
</form>
<h2 className="text">Name: {this.props.name} </h2>
</div>
);
}
}
export default Dogue;
The submitted information is then passed to 'App.jsx', where it is used to update state:
import React, {Component} from 'react';
import './styles.css';
import DogList from './DogList';
import axios from 'axios';
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
};
this.updateStateWithInput = this.updateStateWithInput.bind(this);
}
setData = async () => {
const x = await fetch('https://dog.ceo/api/breed/hound/images');
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true,
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data,
});
}
updateStateWithInput(nameInput) {
//Here is where state is updated.
//change state, then use axios.post to submit data
}
render() {
return this.state.loading ? (
<h1 className="text"> Dogues Loading.....</h1>
) : (
<div>
<h1 className="text">Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction={this.updateStateWithInput}
/>
</div>
);
}
}
export default App;
The updated state, I imagine, will be used in the axios post request to submit data to the database. So, I've got input data being sent from Dogue to App, I'm just not sure what to do now? The information currently in state looks as follows:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
I should also show my map function, in DogList.jsx:
import React from 'react';
import Dogue from './Dogue';
const DogList = (props) => {
return (
<div className="img-container">
{props.dogs.map((doggie, index) => {
return (
<Dogue
id={props.name[index] && props.name[index].id}
key={index}
dogList={doggie}
name={props.name[index] && props.name[index].dogName}
inputFunction={props.inputFunction}
/>
);
})}
</div>
);
};
export default DogList;
You can send a POST request with axios by calling:
axios.post(url, data, options);
It’s similar to the way you called the get method to make a GET request.
I’m leaving this axios cheat sheet here since it’s really useful until you get the hang of it:
https://kapeli.com/cheat_sheets/Axios.docset/Contents/Resources/Documents/index

Gatsby Stripe Checkout - multiple quantities

I'm following this Gatsby guide - https://www.gatsbyjs.org/tutorial/ecommerce-tutorial/
I would like to modify this code to allow the user to change the number of items they can purchase.
Following this React guide - https://reactjs.org/docs/forms.html - I am adding an <input> field to allow a quantity to be chosen.
I then want to pass the value to here - items: [{ sku: "XXXXXXXX", quantity: 1 }]
The error I get is TypeError: _this.handleInputChange is undefined on the line this.handleInputChange = this.handleInputChange.bind(this);
Any pointers would be great, thanks.
import React from "react"
const Checkout = class extends React.Component {
constructor(props) {
super(props);
this.state = {
numberOfItems: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
componentDidMount() {
this.stripe = window.Stripe("XXXXXXX")
}
async redirectToCheckout(event) {
event.preventDefault()
const { error } = await this.stripe.redirectToCheckout({
items: [{ sku: "XXXXXXXX", quantity: 1 }],
successUrl: `http://localhost:8000/thank-you`,
cancelUrl: `http://localhost:8000/`,
})
if (error) {
console.warn("Error:", error)
}
}
render() {
return (
<div>
<label>
Number to buy
<input
name="numberOfItems"
type="number"
value={this.state.numberOfItems}
onChange={this.handleInputChange} />
</label>
<button
onClick={event => this.redirectToCheckout(event)}
>
Buy Now
</button>
</div>
)
}
}
export default Checkout
Update: I went with a different solution in the end:
import React from "react"
const Checkout = class extends React.Component {
state = { count: 1 }
handleIncrement = () => {
this.setState({ count: this.state.count + 1 })
}
handleDecrement = () => {
if(this.state.count > 1){
this.setState({ count: this.state.count - 1 })
}
}
componentDidMount() {
this.stripe = window.Stripe("")
}
async redirectToCheckout(event) {
event.preventDefault()
const { error } = await this.stripe.redirectToCheckout({
items: [{ sku: "", quantity: this.state.count }],
successUrl: `http://localhost:8000/thank-you`,
cancelUrl: `http://localhost:8000/`,
})
if (error) {
console.warn("Error:", error)
}
}
render() {
return (
<div>
<button onClick={this.handleIncrement}>+</button>
<div>
{this.state.count}
</div>
<button onClick={this.handleDecrement}>-</button>
<button
onClick={event => this.redirectToCheckout(event)}
>
Buy
</button>
</div>
)
}
}
export default Checkout
There is no such function handleInputChange in your class.
I believe you forgot to implement it in your class, for example:
import React from 'react';
const Checkout = class extends React.Component {
constructor(props) {
super(props);
this.state = {
numberOfItems: 2,
inputValue
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(e) {
this.setState({ inputValue: e.target.value });
}
render() {
return (
<div>
<label>
Number to buy
<input
value={this.state.inputValue}
onChange={this.handleInputChange}
/>
</label>
</div>
);
}

Passing props to Parent component

I am really novice to React and I am stuck with this one.
I want to pass data from NewAction component to its parent NewActionSet.
I dont know what i am missing.
I am developing an on-boarding platform with a lot a components and I aim to send all the data entered into all the components to a server.
React parent Component:
import React from 'react'
import './NewActionSet.css'
import axios from 'axios'
import { Container, Segment, Header, Input } from 'semantic-ui-react'
import NewAction from './NewAction'
import 'bootstrap/dist/css/bootstrap.min.css'
class NewActionSet extends React.Component {
constructor (props) {
super(props)
this.state = {
actions: [],
actionType: '',
actionValue: '',
creationStatus: undefined
}
}
handleActions = value => {
this.setState({
actionsList: value
})
console.log(this.state.actionsList)
}
handleSubmit = event => {
event.preventDefault()
console.log(this.state)
axios
.post(
'/assistant/actions/',
{ ...this.state.values },
{ headers: {
xsrfHeaderName: 'X-CSRFToken',
xsrfCookieName: 'csrftoken'
},
withCredentials: true
}
)
.then(response => {
console.log(response)
this.setState({
creationStatus: true
})
})
.catch(error => {
console.log(error)
this.setState({
creationStatus: false
})
})
}
addNewAction = () => {
let { actions } = this.state
this.setState({
actions: [...actions, <NewAction onNewAction={this.handleActionstoParent} />]
})
}
handleActionstoParent = (action2Value, selectedAction) => {
this.setState({
actionType : selectedAction,
actionValue : action2Value
})
// console.log(this.state.actionType, this.state.actiondValue)
}
renderActions () {
return this.state.actions.map((action, index) => {
return (
<NewAction
key={index}
type={this.props.actionType}
content={action.content}
onNewAction={this.handleActionstoParent}
/>
)
})
}
render () {
let index = 0
return (
<Container>
<Header> Action sets </Header>
<Header color='grey' as='h3'>
SET #{index + 1}
</Header>
{this.renderActions()}
<button onClick={() => this.addNewAction()}> New Action </button>
</Container>
)
}
}
export default NewActionSet
React child component
import React from 'react'
import './NewActionSet.css'
import { Header, Dropdown } from 'semantic-ui-react'
import NewSpeechText from './NewSpeechText'
import NewAddPageURL from './NewAddPageURL'
import 'bootstrap/dist/css/bootstrap.min.css'
class NewAction extends React.Component {
constructor (props) {
super(props)
this.state = {
availableActions: [
{ key: 1, text: 'Navigate to page', value: 'Navigate to page' },
{ key: 2, text: 'Play speech', value: 'Play speech' }
],
selectedAction: '',
actionValue: '',
currentElement: ''
}
}
handleActionURL = (value) => {
this.setState({
actionValue: value
})
console.log(this.state.selectedAction, this.state.actionValue)
}
handleActionSpeech = (value) => {
this.setState({
actionValue: value
})
console.log(this.state.selectedAction, this.state.actionValue)
}
// Props to pass data to parent component --> NewActionSet.js
handleActionstoParent = (selected) => {
var action2Value = this.state.actionValue;
console.log(action2Value)
var action2Type = this.state.actionType
this.props.onNewAction(action2Value, action2Type)
console.log(action2Type)
// console.log(this.state.actionValue, this.state.selectedAction)
}
handleChange = (e, { value }) => {
let element
this.setState({
selectedAction: value
})
if (value === 'Navigate to page') {
element = <NewAddPageURL onNewAddPageURL={this.handleActionURL} onChange={this.handleActionstoParent()} />
} else if (value === 'Play speech') {
element = <NewSpeechText onNewSpeechText={this.handleActionSpeech} onChange={this.handleActionstoParent()} />
}
this.setState({
currentElement: element
})
}
render () {
const { value } = this.state
let index = 0
return (
<div className='action'>
<div className='container'>
<Header color='grey' as='h4'>
ACTION #{index + 1}
</Header>
<div className='row'>
<div className='col-md-4'>
<Dropdown
onChange={this.handleChange}
options={this.state.availableActions}
placeholder='Choose an action'
selection
value={value}
/>
</div>
<div className='col-md-4' />
<div className='col-md-4' />
</div>
<div style={{ marginBottom: '20px' }} />
{this.state.currentElement}
</div>
</div>
)
}
}
export default NewAction
Can you please assist?
Thanks a lot
The handleActionstoParent function in NewAction component is the problem.
When you send data from child to parent, actually the data is not updated data.
// Props to pass data to parent component --> NewActionSet.js
handleActionstoParent = (e) => {
this.setState({ [e.target.name]: e.target.value }, () => {
var action2Value = this.state.actionValue;
var action2Type = this.state.actionType;
this.props.onNewAction(action2Value, action2Type);
});
}
You could pass a function to NewAction, in example below we pass handleDataFlow function to our child component and then use it in our child component to pass data higher:
import React from 'react'
import './NewActionSet.css'
import { Header, Dropdown } from 'semantic-ui-react'
import NewSpeechText from './NewSpeechText'
import NewAddPageURL from './NewAddPageURL'
import 'bootstrap/dist/css/bootstrap.min.css'
class NewAction extends React.Component {
constructor (props) {
super(props)
this.state = {
availableActions: [
{ key: 1, text: 'Navigate to page', value: 'Navigate to page' },
{ key: 2, text: 'Play speech', value: 'Play speech' }
],
selectedAction: '',
actionValue: '',
currentElement: ''
}
}
handleActionURL = (value) => {
this.setState({
actionValue: value
})
console.log(this.state.selectedAction, this.state.actionValue)
}
handleActionSpeech = (value) => {
this.setState({
actionValue: value
})
console.log(this.state.selectedAction, this.state.actionValue)
}
// Props to pass data to parent component --> NewActionSet.js
handleActionstoParent = (selected) => {
var action2Value = this.state.actionValue;
console.log(action2Value)
var action2Type = this.state.actionType
this.props.onNewAction(action2Value, action2Type)
console.log(action2Type)
// console.log(this.state.actionValue, this.state.selectedAction)
}
handleChange = (e, { value }) => {
let element
this.setState({
selectedAction: value
})
this.props.handleDataFlow(value)
if (value === 'Navigate to page') {
element = <NewAddPageURL onNewAddPageURL={this.handleActionURL} onChange={this.handleActionstoParent()} />
} else if (value === 'Play speech') {
element = <NewSpeechText onNewSpeechText={this.handleActionSpeech} onChange={this.handleActionstoParent()} />
}
this.setState({
currentElement: element
})
}
render () {
const { value } = this.state
let index = 0
return (
<div className='action'>
<div className='container'>
<Header color='grey' as='h4'>
ACTION #{index + 1}
</Header>
<div className='row'>
<div className='col-md-4'>
<Dropdown
onChange={this.handleChange}
options={this.state.availableActions}
placeholder='Choose an action'
selection
value={value}
/>
</div>
<div className='col-md-4' />
<div className='col-md-4' />
</div>
<div style={{ marginBottom: '20px' }} />
{this.state.currentElement}
</div>
</div>
)
}
}
export default NewAction
Data flow in React is unidirectional. Data has one, and only one, way to be transferred: from parent to child.
To update parent state from child you have to send action (in props).
<NewAction updateParentState={this.doSmth} />
...
const doSmth = params => { this.setState({ ... })
and in NewAction you can call it in specific case
let parentUpdateState = ....
this.props.updateParentState(parentUpdateState);

How to add props to state

I have a problem when trying to add my redux array to a component’s state:
componentDidMount() {
this.props.cardAction()
this.setState({ showCards: this.props.cardAction() })
}
hhhhhh undefined console log undefined
Here’s my dashboard code:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import Header from '../../common/Header/'
import Masonry from '../../common/Masonry/'
import { cardAction } from '../../store/actions/Cards'
import Arrow_Down from '../../assets/img/arrow-down.svg'
class Dashboard extends Component {
componentDidMount() {
this.props.cardAction()
this.setState({ showCards: this.props.cardAction() })
}
constructor(props) {
super(props)
this.state = {
collapsed: true,
class: 'collapsed',
showCards: {},
}
this.toggleCollapse = this.toggleCollapse.bind(this);
}
toggleCollapse(i, info) {
console.log('i', info, 'iiiii', i)
this.setState({
collapsed: !this.state.collapsed,
class: this.state.collapsed ? '' : 'collapsed',
showCards: info
}, () => {
// my state is updated here !
console.log('cardsss', this.state.showCards)
})
if (this.state.showCards === 'active') {
let carddd = this.state.showCards
this.setState({
showCards: {
...this.state.showCards,
open: 'inactive'
}
});
}
else {
this.setState({
showCards: {
...this.state.showCards,
open: 'active'
}
});
}
}
render() {
console.log('hhhhhh', this.state.showCards)
const cardList = this.props.Cards.map((info, i) => {
return (
<div className={(info.open === 'active') ? 'collapsed' : ''} key={i}>
<div className={(info.open === 'active') ? 'header flex space-between active' : 'header flex space-between'}>
<h2>{info.title}</h2>
<span onClick={() => { this.toggleCollapse(i, info) }}><img src={Arrow_Down} alt='Arrow' /></span>
</div>
<div className='content'>
<p>{info.description}</p>
</div>
</div>
)
})
return (
<div>
<Header />
<Masonry columns={3} gap={20}>
{cardList}
</Masonry>
</div>
)
}
}
Dashboard.defaultProps = {
columns: 2,
gap: 20,
Cards: []
}
Dashboard.propTypes = {
Cards: PropTypes.array.isRequired,
}
const mapStateToProps = state => {
return { Cards: state.cards.result }
}
const mapDispatchToProps = dispatch => ({
cardAction: () => dispatch(cardAction())
})
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard)
this.props.cardAction() is a redux action, it's not meant for you to directly assign to state, reason is redux action will return to reducer, not component. You should remove the setState in componentDidMount
componentDidMount() {
this.props.cardAction();
}
When you invoked this.props.cardAction(), it will call the function that defined in redux action file, and the result will be available at this.props.Cards as you mentioned above
const mapStateToProps = state => {
return { Cards: state.cards.result }
}

React force componentDidMount

I have the following:
import React from 'react';
import axios from 'axios';
class FirstName extends React.Component {
constructor(props) {
super(props);
this.state = {
submitted: false
};
}
getName () {
var name = this.refs.firstName.value;
this.setState(function() {
this.props.action(name);
});
}
handleSubmit (e) {
e.preventDefault();
this.setState({ submitted: true }, function() {
this.props.actionID(2);
this.props.activeNav('color');
});
}
render () {
return (
<div>
<h2>tell us your first name</h2>
<form>
<input
type="text"
ref="firstName"
onChange={this.getName.bind(this)}
/>
<div className="buttons-wrapper">
<button href="#">back</button>
<button onClick={this.handleSubmit.bind(this)}>continue</button>
</div>
</form>
</div>
);
}
};
class PickColor extends React.Component {
backToPrevious (e) {
e.preventDefault();
this.props.actionID(1);
this.props.activeNav('name');
}
goToNext (e) {
e.preventDefault();
this.props.actionID(3);
this.props.activeNav('design');
this.props.displayIconsHolder(true);
}
getColorValue(event) {
this.props.color(event.target.getAttribute("data-color"));
}
render () {
var colors = ['red', 'purple', 'yellow', 'green', 'blue'],
colorsLink = [];
colors.forEach(el => {
colorsLink.push(<li
data-color={el}
key={el}
onClick={this.getColorValue.bind(this)}
ref={el}>
{el}
</li>
);
});
return (
<section>
<ul>
{colorsLink}
</ul>
<button onClick={this.backToPrevious.bind(this)}>back</button>
<button onClick={this.goToNext.bind(this)}>continue</button>
</section>
);
}
}
class ConfirmSingleIcon extends React.Component {
goBack () {
this.props.goBack();
}
confirmCaptionandIcon (event) {
var optionID = event.target.getAttribute("data-option-id"),
name = event.target.getAttribute("data-option-name");
this.props.setOptionID(optionID);
this.props.setIcon(1, name, optionID, false);
}
goNext () {
this.props.goNext();
}
render () {
console.log(this.props.currentState);
var options = [],
that = this;
this.props.iconOptionsList.forEach(function(el){
options.push(<li onClick={that.confirmCaptionandIcon.bind(that)} key={el.option} data-option-name={el.option} data-option-id={el.id}>{el.option}</li>);
});
return (
<div>
<h2>Choose your caption</h2>
<h3>
{this.props.selectedIcon}
</h3>
<ul>
{options}
</ul>
<button onClick={this.goBack.bind(this)} >back</button>
<button onClick={this.goNext.bind(this)} >confirm</button>
</div>
);
}
}
class ConfirmCaption extends React.Component {
handleClick () {
var currentState = this.props.currentState;
this.props.setIcon(currentState.icon_ID, currentState.selectedIcon, currentState.option_ID, true);
this.props.setIconVisiblity(true);
this.props.setIconListVisiblity(false);
}
render () {
console.log(this.props.currentState);
return (
<div>
<p onClick={this.handleClick.bind(this)}>confirm icon and caption</p>
</div>
);
}
}
class ChooseIcon extends React.Component {
constructor(props) {
super(props);
this.state = {
icons: [],
iconList: true,
confirmIcon: false,
confirmCaption: false,
selectedIconOptions: '',
icon_ID: '',
option_ID: '',
selectedIcon: ''
};
this.setOptionID = this.setOptionID.bind(this);
this.setIconVisiblity = this.setIconVisiblity.bind(this);
this.setIconListVisiblity = this.setIconListVisiblity.bind(this);
}
setOptionID (id) {
this.setState({ option_ID: id })
}
setIconVisiblity (onOff) {
this.setState({ confirmIcon: onOff })
}
setIconListVisiblity (onOff) {
this.setState({ iconList: onOff })
}
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
handleClick (event) {
var iconId = event.target.getAttribute("data-icon-id"),
that = this;
this.state.icons.forEach(function(el){
if(el.id == iconId){
that.setState(
{
confirmIcon: true,
iconList: false,
selectedIcon: el.name,
icon_ID: iconId,
selectedIconOptions: el.option
}
);
}
});
}
goBack () {
this.setState(
{
confirmIcon: false,
iconList: true
}
);
}
goNext () {
this.setState(
{
confirmIcon: false,
iconList: false,
confirmCaption: true
}
);
}
render () {
var icons = [];
this.state.icons.forEach(el => {
icons.push(<li data-icon-id={el.id} onClick={this.handleClick.bind(this)} key={el.name}>{el.name}</li>);
});
return (
<div>
{this.state.iconList ? <IconList icons={icons} /> : ''}
{this.state.confirmIcon ? <ConfirmSingleIcon goBack={this.goBack.bind(this)}
goNext={this.goNext.bind(this)}
setIcon={this.props.setIcon}
selectedIcon={this.state.selectedIcon}
iconOptionsList ={this.state.selectedIconOptions}
setOptionID={this.setOptionID}
currentState={this.state} /> : ''}
{this.state.confirmCaption ? <ConfirmCaption currentState={this.state}
setIcon={this.props.setIcon}
setIconVisiblity={this.setIconVisiblity}
setIconListVisiblity={this.setIconListVisiblity} /> : ''}
</div>
);
}
}
class IconList extends React.Component {
render () {
return (
<div>
<h2>Pick your icon</h2>
<ul>
{this.props.icons}
</ul>
</div>
);
}
}
class Forms extends React.Component {
render () {
var form;
switch(this.props.formID) {
case 1:
form = <FirstName action={this.props.action} actionID={this.props.switchComponent} activeNav={this.props.activeNav} />
break;
case 2:
form = <PickColor displayIconsHolder={this.props.seticonsHolder} color={this.props.colorVal} actionID={this.props.switchComponent} activeNav={this.props.activeNav} />
break;
case 3:
form = <ChooseIcon setIcon={this.props.setOptionA} />
break;
}
return (
<section>
{form}
</section>
);
}
}
export default Forms;
"ChooseIcon" is a component that will get used 3 times therefore everytime I get to it I need to bring its state back as if it was the first time.
Ideally I would need to make this ajax call everytime:
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
is there a way to manually call componentDidMount perhaps from a parent component?
React handles component lifecycle through key attribute. For example:
<ChooseIcon key={this.props.formID} setIcon={this.props.setOptionA} />
So every time your key (it can be anything you like, but unique) is changed component will unmount and mount again, with this you can easily control componentDidMount callback.
If you are using the ChooseIcon component 3 times inside the same parent component, I would suggest you to do the ajax in componentDidMount of the parent component like this (exaclty how you have in your example, in terms of code)
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
and then pass this data down to the ChooseIcon component
render() {
return (
//do your stuff
<ChooseIcon icons={this.state.icons}/>
)
}
after this you will only need to set the received props in your ChooseIconcomponent, for that you only need to change one line in it's constructor:
constructor(props) {
super(props);
this.state = {
icons: props.icons, // Changed here!
iconList: true,
confirmIcon: false,
confirmCaption: false,
selectedIconOptions: '',
icon_ID: '',
option_ID: '',
selectedIcon: ''
};
this.setOptionID = this.setOptionID.bind(this);
this.setIconVisiblity = this.setIconVisiblity.bind(this);
this.setIconListVisiblity = this.setIconListVisiblity.bind(this);
}
The parent component can use a ref to call the function directly.
However, trying to force this function feels like a smell. Perhaps lifting the state higher up the component tree would solve this problem. This way, the parent component will tell ChooseIcon what to show, and there will not be a need to call componentDidMount again. Also, I assume the Ajax call can also occur once.

Resources