How to pass spinning bar to another component in ReactJS - reactjs

I am in a scenario where I have to add a spinning bar in the component say,
List.js
class List extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
//spinning bar should be displayed here
</div>
);
}}
But the spinning bar should be displayed when another method in Actions(i.e redux) is called. So How will I pass this from actions.js to the render component in List.js
Actions.js
export const getList = (listInfo) => dispatch => {
//Spinning should start here
return application.getClientInfo(userInfo).then(
listInfo => {
//spinning should stop here
return dispatch(getListInfo(listInfo))
},
error => {
return dispatch(apologize('Error in getting application'))
}
)
}
getList and ListComponent is called in main.js
main.js
render() {
this.props.getClientApplication(this.props.user);
return (
<div>
<List />
</div>
);
}
So how will I add render method here that is actually to be displayed in list.js? Please help

In your reducer, keep a loading state and dispatch an action to set and clear loading states as and when you want
class List extends Component {
constructor(props) {
super(props);
}
render() {
const { isLoading } = this.props;
return (
<div>
//spinning bar should be displayed here
{isLoading && <Spinner>}
</div>
);
}
}
Actions.js
export const spinner = isLoading => {
return {
type: actionType.SPINNER, isLoading
}
}
export const getList = (listInfo) => dispatch => {
//dispatch loading action
dispatch(spinner(true));
return application.getClientInfo(userInfo).then(
listInfo => {
dispatch(spinner(false))
return dispatch(getListInfo(listInfo))
},
error => {
dispatch(spinner(false))
return dispatch(apologize('Error in getting application'))
}
)
}
Also make sure you aren't dispatching an action in render without using suspense
render() {
this.props.getClientApplication(this.props.user);
return (
<div>
<List isLoading={this.props.isLoading} />
</div>
);
}

Related

How to make components load only once in React?

I am using React to realize following: in the Main page, if users click one button, will load Child page, if not then load another child page.
The problem is the Child component is loading repeatedly because I can check in console that the 'load function' is being printed nonstop. How can I let the child component only load(refresh) once when user clicks the button? Thanks!
In main.jsx:
import {Child} from "../Child";
export const Main = (props) => {
....
if (props.ButtonClicked) {
showPortal = <Child />
} else {
showPortal = <AnotherChild />
}
....
}
in Child.jsx:
export const Child = ()=> {
console.log('load function')
return (<div>test</div>)
}
Not sure what are trying to do because you haven't post your whole code.
But something like this should work.
export default class Comp extends React.Component {
constructor(props) {
super(props);
this.state = {
clicked: false,
};
}
onClickFunction() {
this.setState({
clicked: true,
});
}
render() {
const d = this.state.clicked && <Child />;
return (
<div>
<button onClick={this.onClickFunction.bind(this)}></button>
{d}
</div>
);
}
}
const Child = () => {
console.log("load function");
return <div>test</div>;
};

Pass state value to component

I am really new in React.js. I wanna pass a state (that i set from api data before) to a component so value of selectable list can dynamically fill from my api data. Here is my code for fetching data :
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
From that code, i set a state called item. And i want to pass this state to a component. Here is my code :
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
But i get an error that say
TypeError: Cannot read property 'item' of undefined
I am sorry for my bad explanation. But if you get my point, i am really looking forward for your solution.
Here is my full code for additional info :
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {List, ListItem, makeSelectable} from 'material-ui/List';
import Subheader from 'material-ui/Subheader';
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.getListSiswa();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
export default ListSiswa;
One way to do it is by having the state defined in the parent component instead and pass it down to the child via props:
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.props.fetchItem();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
{this.props.item}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
class ListSiswa extends Component {
state = {
item: {}
}
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
render() {
return (
<SelectableList item={this.state.item} fetchItem={this.getListSiswa}>
<Subheader>Daftar Siswa</Subheader>
</SelectableList>
);
}
}
export default ListSiswa;
Notice that in wrapState now I'm accessing the state using this.props.item and this.props.fetchItem. This practice is also known as prop drilling in React and it will be an issue once your app scales and multiple nested components. For scaling up you might want to consider using Redux or the Context API. Hope that helps!
The error is in this component.
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
This component is referred as Stateless Functional Components (Read)
It is simply a pure function which receives some data and returns the jsx.
you do not have the access this here.

How to initialize state with api's data

I'm creating a React/Redux app that fetches data from an api (pokeapi.co). I fetch data using axios. When I display data on react components, it results in an error that data is undefined. After some digging, I find that my state at first returns initial state which is empty object then it returns the api data. but it dont display on react. I'm new to React so I'm guessing it has to do with the axios asynchronous functionality. How do you set state with api's initial data or wait on rendering the data till state has api's data?
Here is the Reducer
function pokemonReducer(state={}, action) {
switch (action.type) {
case pokemonsActions.GET_POKEMON_SUCCESS:
{
return {...state, data: action.payload.data}
}
default:
{
return state;
}
}
}
export default pokemonReducer
Here is the action
export const GET_POKEMON_SUCCESS = 'GET_POKEMON_SUCCESS'
export const GET_POKEMON_ERROR = 'GET_POKEMON_ERROR'
function getPokemonSuccess(response) {
return {
type: GET_POKEMON_SUCCESS,
payload: response
}
}
function getPokemonError(err) {
return {
type: GET_POKEMON_ERROR,
payload: err
}
}
export function getPokemon() {
return (disp,getState) =>
{
return pokeAPI.getPokeAPI()
.then((response) => { disp(getPokemonSuccess(response))})
.catch((err)=> disp(getPokemonError(err)))
}
}
Store
const loggerMiddleware = createLogger()
const middleWare= applyMiddleware(thunkMiddleware,loggerMiddleware);
const store = createStore(rootReducer,preloadedState,
compose(middleWare, typeof window === 'object' && typeof window.devToolsExtension !== 'undefined'
? window.devToolsExtension() : (f) => f
))
const preloadedState=store.dispatch(pokemonActions.getPokemon())
export default store
in React component
function mapStateToProps(state) {
return {
pokemons:state.pokemons
}
}
class PokemonAbility extends React.Component {
render(){
return (
<div>
<div className="header">
<h1>Fetch Poke Api with axios</h1>
</div>
<main>
<h3> Display pokemons abilities </h3>
<p>{this.props.pokemons.data.count}</p>
</main>
</div>
)
}
}
export default connect(
mapStateToProps
)(PokemonAbility)
Api data example
{
"count": 292,
"previous": null,
"results": [
{
"url": "https://pokeapi.co/api/v2/ability/1/",
"name": "stench"
},
{
"url": "https://pokeapi.co/api/v2/ability/2/",
"name": "drizzle"
},
{
"url": "https://pokeapi.co/api/v2/ability/3/",
"name": "speed-boost"
}
],
"next": "https://pokeapi.co/api/v2/ability/?limit=20&offset=20"
}
You're rendering your component before the data has loaded. There are many strategies for dealing with this. In no particular order, here are some examples:
1. Short circuit the render
You can short circuit the render by returning a loading message if the data isn't there:
function mapStateToProps(state) {
return {
pokemons:state.pokemons
}
}
class PokemonAbility extends React.Component {
render(){
if (!this.props.pokemons.data) {
return (
<div>Loading...</div>
);
}
return (
<div>
<div className="header">
<h1>Fetch Poke Api with axios</h1>
</div>
<main>
<h3> Display pokemons abilities </h3>
<p>{this.props.pokemons.data.count}</p>
</main>
</div>
);
}
}
export default connect(mapStateToProps)(PokemonAbility);
2. Lift the data check to a parent component
You can move the mapStateToProps into a higher component, or abstract out the view component, and only render the view when the data is ready:
function mapStateToProps(state) {
return {
pokemons:state.pokemons
}
}
class SomeHigherComponent extends React.Component {
render(){
return (
this.props.pokemons.data ?
<PokemonAbility pokemons={this.props.pokemons} /> :
<div>Loading...</div>
);
}
}
3. Higher order component data checking
You could wrap your components in a "higher order component" (a function that takes a component class and returns a component class) to check if that prop exists before rendering:
function EnsurePokemon(ChildComponent) {
return class PokemonEnsureWrapper extends React.Component {
render() {
return (
this.props.pokemons.data ?
<ChildComponent {...this.props} /> :
<div>Loading...</div>
);
}
}
}
Usage:
export default connect(
mapStateToProps
)(EnsurePokemon(PokemonAbility))
And you can wrap any child component in this EnsurePokemon HOC to make sure it doesn't render until the data has loaded.

I can not access the right data of property sent from parent to child component

I am facing an issue with react and I am totally stuck. I have 3 components: channel as a parent and header and story as a children:
class Channel extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
this.props.getChannels();
}
render() {
return (
<div>
<div className="col-xs-12 col-md-8 col-lg-8>
<div className="row">
<Header activeChannelList={this.props.channels.channelsArr}/>
</div>
<div className="row">
{
this.props.channels.channelsArr.map((item, i) => <StoryBoard
newsChanel={item}
key={"storyBoard" + i}
></StoryBoard>)
}
</div>
</div>
<div className="col-xs-12 col-md-2 col-lg-2 color2">.col-sm-4</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
channels: state.channelReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
getChannels: () => {
dispatch(getChannels());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Channel);
As you can see I have a ajax call with this.props.getChannels(); and I put it in componentDidMount to make sure that it is called before rendering then after I pass the channels to the Header ans story which are children components.
Now my problem is when I try to access it in Header via console.log(this.props.activeChannelList); I get 0 thought I should have 5 channels. More intrestingly when I try to access the props I send in Stroryboard I can easily access them without any problem. The following is my code for Header:
export class Header extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
console.log("dddddddddddddddddddddddddddddddddddddddddd");
console.log(this.props.activeChannelList);// I get 0 though I should get 5
}
render() {
return (
<div className="col-xs-12 header tjHeaderDummy">
<div className="col-xs-1"></div>
</div>
);
}
}
And my storyboard is :
class StoryBoard extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
if(this.props.isFreshLoad ){
do sth
}
}
render() {
return (
<div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
stories: state.storyBoardReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
//some funcs
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(StoryBoard);
Can anyone help?
U r printing the value in componentDidMount method in Header component, this lifecycle method get called only once, if ur api response come after the rendering of Header, it will never print 5, put the console in render method, so that at any time when u get the response it will populate the value.
From Docs:
componentDidMount: is invoked immediately after a component is mounted
first time. This is where AJAX requests and DOM or state updates
should occur.
Try this Header Comp, it will print the proper value:
export class Header extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
}
render() {
return (
<div className="col-xs-12">
{this.props.activeChannelList}
</div>
);
}
}

How can I make each child of component has a state? react redux

In my project, there are HomeIndexView and table component. So, when a user logs in to his account, in HomeIndexView, it shows all tables in the database. What I want to do is that make each table have a state so that it changes color of depends on its state(depends on child's state)... How can I do this?
My table component has a state like below.
const initialState = {
allTables: [],
showForm: false,
fetching: true,
formErrors: null,
};
EDIT ---1
HomeIndexView
class HomeIndexView extends React.Component {
componentDidMount() {
setDocumentTitle('Table_show');
}
componentWillunmount() {
this.props.dispatch(Actions.reset());
}
_renderAllTables() {
const { fetching } = this.props;
let content = false;
if(!fetching) {
content = (
<div className="tables-wrapper">
{::this._renderTables(this.props.tables.allTables)}
</div>
);
}
return (
<section>
<header className="view-header">
<h3>All Tables</h3>
</header>
{content}
</section>
);
}
_renderTables(tables) {
return tables.map((table) => {
return <Table
key={table.id}
dispatch={this.props.dispatch}
{...table} />;
});
}
render() {
return (
<div className="view-container tables index">
{::this._renderAllTables()}
</div>
);
}
}
EDIT--2
_handleClick () {
const { dispatch } = this.props;
const data = {
table_id: this.props.id,
};
if (this.props.current_order == null) {
dispatch(Actions.create(data));
Object.assign({}, this.state, {
tableBusy: true
});
}
else{
this.props.dispatch(push(`/orders/${this.props.current_order}`));
}
}
The state you shared above is part of the global state (where tableReducer use) not the table's component state, so what you need is to initialize component state in Table React component, so that you can check some values to render css differently something like this:
import React from "react";
class TableComponent extends React.Component {
componentWillMount() {
this.setInitialState();
}
setInitialState() {
this.setState({ isWhatever: false });
}
render() {
return (
<div>
<h1 classname={this.state.isWhatever ? 'css-class' : 'another-class'}>
{this.props.id}
</h1>
</div>
);
}
}

Resources