I am not able to dynamically AJAX loaded HTML format data(as mentioned below) in dangerouslySetInnerHTML(ie., it is setting in DOM as the same string and not compiling). Please note that I am using axios plugin for AJAX API calls. When I try with the same HTML data giving statically in dangerouslySetInnerHTML, it is working perfectly. I don't know what is going there.
[{id: 1, image: "assets/img/news-sm.png", content: "<p>சிறுவனே!</p>"}]
My React Component code is
import React, { Component, PropTypes } from 'react';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import Divider from 'material-ui/Divider';
import { Media } from 'react-bootstrap';
import { map } from 'lodash';
class NewsList extends Component {
constructor(props) {
super(props);
}
handleListClick(d){
console.log(d);
}
renderLoadingComponent() {
return <ListItem style={{textAlign: 'center'}}>Loading...</ListItem>;
}
renderNothingFoundElement() {
return <ListItem style={{textAlign: 'center'}}>Nothing found</ListItem>;
}
render() {
const { data, loading } = this.props;
return (
<List className="list">
{
map(data, (d, index) =>
React.Children.toArray([
<ListItem className="list-item">
<Media onClick={(ev) => this.handleListClick(d)} className="media-align">
<Media.Left align="middle">
<img width={75} height={56} src={d.image} alt="news-image"/>
</Media.Left>
<Media.Body>
{d.content && <p dangerouslySetInnerHTML={{ __html: d.content }}></p>}
</Media.Body>
</Media>
</ListItem>,
(index + 1) !== data.length ? <Divider /> : ''
])
)
}
{ loading ? this.renderLoadingComponent() : data.length === 0 ? this.renderNothingFoundElement() : '' }
</List>
)
}
}
NewsList.propTypes = {
data: React.PropTypes.array.isRequired
};
export default NewsList;
I am calling the above Component from another Component like this
import React, { Component, PropTypes } from 'react';
import NewsList from '../components/news-list';
class Home extends Component {
constructor(props) {
super(props);
this.state = { ajaxData: [], ajaxLoading: false }
}
componentDidMount() {
axios.get('/ajaxUrl')
.then(response => this.setState({ajaxData: response.data}))
}
render() {
return (
<NewsList data={this.state.ajaxData} loading={this.state.ajaxLoading} />
)
}
}
Can someone please help me to solve this issue? Thanks.
Probably you have to specify The Component Lifecycle shouldComponentUpdate or forceUpdate or componentWillReceiveProps facebook.github.io
But you can also use a huck and set state in your Parent component to '' and on a callback set the NewList and pass props
this.setState({newList: ''},()=> this.setState({ newList: <NewList {...props}/>}))
Update The simple one
class Home extends Component {
constructor(props) {
super(props);
this.state = { ajaxData: [], ajaxLoading: false, newsList:'' }
}
componentDidMount() {
axios.get('/ajaxUrl')
.then(response => this.setState({ajaxData: response.data},
()=> this.setState({newsList: <NewsList data={this.state.ajaxData} loading={this.state.ajaxLoading} />})))
}
render() {
return (
<div> {this.state.newsList} <div/>
)
}}
Also you can implement method for a life cycle
shouldComponentUpdate(nextProps, nextState) {
return this.state.ajaxData != nextState.ajaxData;
}
Related
I'm trying to assign props that I get from parent component and assign it to state in child component(because I want to manipulate the props data I assign it to state first).
When I log the state variable it comes out as an empty array but when I make a new variable in render and assign props to it and log it. It does show the data I need. Also, when I just log this.props I can definitely see that props holds the data I need.
I've assigned props to state a couple of times before, so I'm not sure what is so different this time for it not to work.
Parent component where I pass props to child:
<ShowAvailableTimeslots onClick={this.getSelectedTimeslot} allTimeSlots={this.state.AvailabletimeSlots} />
Child component where I try to assign props to state:
class ShowAvailableTimeslots extends Component {
constructor(props) {
super(props)
this.state = {
sliceEnd: 5,
sliceStart:0,
selectedSlotValue: "",
timeSlotArr: this.props.allTimeSlots,
// timeSlotSlice: timeSlotArr.slice(this.state.sliceStart, this.state.sliceEnd)
}
}
handleTimeSlotClick = (timeSlot) => {
this.setState({ selectedSlotValue: timeSlot }, () => {
this.props.onClick(this.state.selectedSlotValue)
console.log('time slot value', timeSlot)
});
}
previousSlots =()=>{
var test;
}
forwordSlots =()=>{
var test;
}
render() {
var timeSlotArrRender = this.props.allTimeSlots;
return (
<React.Fragment>
{console.log("state", this.state.timeSlotArr)} // --> doesn't show data
{console.log("props", this.props)} // --> does show data
{console.log("render variable", timeSlotArrRender )} // --> does show data
<button className="button btn" onClick={() => this.previousSlots()} disabled={this.state.sliceStart === 0}>left</button>
{/* {this.state.timeSlotArr.map(timeSlot => <a className="timeslot btn " key={timeSlot} value={timeSlot} onClick={() => this.handleTimeSlotClick(timeSlot)}>{timeSlot}</a>)
} */}
<button className="button btn">right</button>
</React.Fragment>
)
}
}
export default ShowAvailableTimeslots
the constructor is called when the component life cycle begins.
You are passing the this.state.AvailabletimeSlots from the parent and by then the constructor have already been called and the assignment for timeSlotArr is already done, so
timeSlotArr: this.props.allTimeSlots // will not work
You have to get help of life cycle methods or hooks
componentWillReceiveProps(nextProps){
this.setState({timeSlotArr: nextProps.allTimeSlots })
}
According to new changes you have to use
static getDerivedStateFromProps(nextProps, prevState){
return {
timeSlotArr: nextProps.allTimeSlots
};
}
I have it working just fine. https://jsfiddle.net/85zc4Lxb/
class App extends React.Component {
render() {
return (<Child passing="I am being passed to child" />);
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
passedProp: this.props.passing,
}
}
render() {
return (
<React.Fragment>
<button>{this.state.passedProp}</button>
</React.Fragment>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
I try to recreate the scenario and it work try saving all your files again and then check
parents component
import React, { Component } from "react";
import TestOne from "./Components/TestOne/TestOne";
export class App extends Component {
state = {
x: "x data",
y: "y data",
};
render() {
return (
<div>
<TestOne x={this.state.x} allTimeSlots={this.state.y}/>
</div>
);
}
}
export default App;
Child component
import React, { Component } from "react";
export class TestOne extends Component {
constructor(props) {
super(props);
this.state = {
sliceEnd: 5,
sliceStart: 0,
selectedSlotValue: "",
timeSlotArr: this.props.x,
};
}
render() {
var timeSlotArrRender = this.props.allTimeSlots;
return (
<React.Fragment>
{console.log("state", this.state.timeSlotArr)}
{console.log("props", this.props)}
{console.log("render variable", timeSlotArrRender)}
<button className="button btn">right</button>
</React.Fragment>
);
}
}
export default TestOne;
Result:
I think you are missing this.setState({})
I have two commponents. Map and LfMap.
import React, { Component } from 'react';
import { LfMap } from '../../components/Localisation/LfMap';
export class Map extends Component {
constructor(props) {
super(props);
this.state = {
dev: [],
};
}
componentDidMount() {
fetch("URL")
.then(resp => resp.json())
.then(pas => this.setState({dev: pas}));
}
render() {
return(
<React.Fragment>
<LfMap list={this.state.dev}/>
</React.Fragment>
);
}
}
import React from 'react';
import { Map, CircleMarker, TileLayer, Polyline} from "react-leaflet";
import "leaflet/dist/leaflet.css";
import Control from 'react-leaflet-control';
class LfMap extends React.Component {
constructor(props) {
super(props);
this.state = {
devices: this.props.list,
}
}
render() {
return(
<React.Fragment>
<Map
style={{ height: "480px", width: "100%" }}
zoom={12}
center={[22.22, 21.00]}
>
<TileLayer url="http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
</Map>
</React.Fragment>
);
}
}
export { LfMap };
And I'm passing a prop list to LfMap with list of my objects.
But when I'm trying to console.log(this.state.devices) it shows Undefined but it should console my list of objets which i fetched in other component. Why it's happening?
Why set state = props ? is an anti-pattern, you should keep using this.prop.list in the other component.
Do this:
console.log(this.props.list)
it will print [], and then when the results come it will print the full array.
When you need to use this array always use this.props.list.
Based in your comment here is how you give solution to that:
At the parent you add a function
listUpdate(newList){
this.setState({
list: newList
})
}
and then you pass this function to the child
<LfMap list={this.state.dev} listUpdate={this.listUpdate}/>
when you need to update it you need to call this.props.listUpdate.
Always update the props :) that's where the reference is, this is the pattern you should follow.
I am running a client and server setup (react and axios api calls) And I would like to understand how to access the returned data from my child components within the React Framework. I have the connection working to the http server, however i lack the foundational knowledge of working with this.state or props.
here is effectively my app.js
import React, { Component } from 'react';
import axios from 'axios';
import ChildComponent from "./components/childComponent"
class App extends Component {
state = {
data: [],
intervalIsSet : false
};
componentDidMount() {
this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 10000);
this.setState({ intervalIsSet: interval });
}
}
getDataFromDb = () => {
fetch("http://localhost:3001/api/getData")
.then(data => data.json())
.then(res => this.setState({ data: res.data }));
};
render() {
const { data } = this.state;
return (
<div>
<childComponent />
</div>
);
};
}
export default App;
and here is the child component. --> my intention is to simply access (or print out) my returned data from the server from within the child component.
import React, { Component } from 'react';
class ChildComponent extends React.Component {
render() {
return (
{this.props.data}
);
}
}
export default ChildComponent;
First make sure you uppercase the first letter of ChildComponent. If you want to pass data you should add that object as an attribute to the component, and then access it throught this.props. Also you need to render a single top element, and if you don't need div or any other html element, you can wrap it with React.Fragment.
Regarding to data, if its an array you can simply map through it and return data you want to see, if you want the entire object to show as a string, you can use JSON.stringify(). And if that's an object you can show only data you want.
class App extends React.Component {
//...
render() {
const { data } = this.state;
return (
<div>
<ChildComponent data={data} />
</div>
);
}
}
//for array, ex: data = ["first","name"];
class ChildComponent extends React.Component {
render() {
return (
<React.Fragment>
{this.props.data.map(item =>
<p>{item}</p>
)}
</React.Fragment>
);
}
}
//for object, ex: data = {id: 1, name: 'John'};
class ChildComponent extends React.Component {
render() {
const {data} = this.props;
return (
<React.Fragment>
<p>{data.id}</p>
<p>{data.name}</p>
</React.Fragment>
);
}
}
//for single value (string, int, etc), ex: data = "my name";
class ChildComponent extends React.Component {
render() {
return (
<React.Fragment>
<p>{this.props.data}</p>
</React.Fragment>
);
}
}
//to show object as a string (could be any object mentioned before)
class ChildComponent extends React.Component {
render() {
return (
<React.Fragment>
{JSON.stringify(this.props.data)}
</React.Fragment>
);
}
}
You can pass down the data array as the data prop to the child component. Make sure you also uppercase the first character in the component name, or it won't work properly.
A component needs to render a single top level element, so you could e.g. render a div with the JSON stringified data prop inside of it in the child component.
class App extends React.Component {
// ...
render() {
const { data } = this.state;
return (
<div>
<ChildComponent data={data} />
</div>
);
}
}
class ChildComponent extends React.Component {
render() {
return <div>{JSON.stringify(this.props.data)}</div>;
}
}
I am currently in a project, and I have had to do null checks on every single props that has come in to children components wether through redux or passing it in myself. I feel like that is not normal with react? Isn't a huge plus side of React is automatic re-rendering? If I try to put anything into state, I can't because There has to be a null check in the render before I do anything with the data. Thanks in advance!!
PARENT COMPONENT =
class App extends Component {
componentDidMount(){
//where I load the data
this.loadCardsFromServer();
this.props.GetAllData();
}
render() {
//NEED TO DO A NULL CHECK FROM THIS COMING FROM REDUX
const filteredData = !!this.state.data ? this.state.data.filter(card =>{
return card.name.toUpperCase().includes(this.state.input.toUpperCase())
}) : null;
return (
//MAKES ME DO ANOTHER NULL CHECK
<div>
{!!this.state.data ? filteredData.map(i => <Card person={i} key={i.created} planet={this.props.planets} />) : null}
</div>
))}
CHILD COMPONENT OF CARD
class Card extends Component {
//WHERE I WANT TO PUT THE PROPS
constructor(){
super();
this.state={
edit: false,
name: this.props.person.name,
birthYear: this.props.person.birth_year
}
}
render() {
let world = null;
//ANOTHER NULL CHECK
if(this.props.planet){
this.props.planet.map(i => {
if(i.id === this.props.person.id){
world = i.name
}
})
}
return (
<div>
//THIS IS WHERE I WANT THE VALUE TO BE STATE
{this.state.edit ? <input label="Name" value={this.state.name}/> : <div className='card-name'>{name}</div>}
</div>
You need to update state when data arrive.
You can do it like this:
import React, { Component } from 'react';
import './App.scss';
import Card from './Components/Card/Card.js';
class App extends Component {
constructor(){
super();
this.state = {
loading:true,
cards:[]
};
}
componentDidMount(){
this.loadCardsFromServer();
}
loadCardsFromServer = () => {
let cardsResponseArray = [];
// fetch your cards here, and when you get data:
// cardsResponseArray = filterFunction(response); // make function to filter
cardsResponseArray = [{id:1,name:'aaa'},{id:2,name:'bbb'}];
setTimeout(function () {
this.setState({
loading:false,
cards: cardsResponseArray
});
}.bind(this), 2000)
};
render() {
if(this.state.loading === true){
return(
<h1>loading !!!!!!!!</h1>
);
} else {
return (
<div>
{this.state.cards.map(card => (
<Card key={card.id} card={card}></Card>
))}
</div>
);
}
}
}
export default App;
And then in your Card component:
import React, { Component } from 'react';
class Card extends Component {
constructor(props){
super(props);
this.props = props;
this.state = {
id:this.props.card.id,
name:this.props.card.name
};
}
render() {
return (
<div className={'class'} >
Card Id = {this.state.id}, Card name = {this.state.name}
</div>
);
}
}
export default Card;
For those interested about React state and lifecycle methods go here
Okay, in this case i craft a little helper's for waiting state in my redux store. I fetch data somewhere (app) and i render a connected component waiting for fetched data in store:
const Loader = (props) => {
if (!props.loaded) {
return null;
}
<Card data={props.data}/>
}
const CardLoader = connect (state => {
return {
loaded: state.data !== undefined
data: state.data
}
})(Loader)
<CardLoader />
I have the following react component which creates a list of Tasks.
The code works ok this.props.data and when the data is empty, no Task appears.
I would like to change the code in a way so that if the array is empty a single text
"list empty"
is displayed instead.
I have tried to create a function for listItems and inside add some logic, but I cannot call it from JXS, example <div>{listItems()}</div> although I am not even sure if this is the correct approach.
Any ideas?
import React, { Component } from 'react';
import Task from './Task.js'
class TasksList extends Component {
constructor(props) {
super(props);
}
render() {
const data = this.props.data;
const listItems = data.map(todo => {
return <Task
id={todo.id}
key={todo.id.toString()}
title={todo.title}
onTitleChange={this.props.onTitleChange}
onTaskDelete={this.props.onTaskDelete}
/>
});
return (
<div>{listItems}</div>
)
}
}
export default TasksList;
This should works:
const listItems = data.length == 0 ? "List empty" : data.map(todo => { ... });
You can use something like below, hope this helps.
class TasksList extends Component {
constructor(props) {
super(props);
}
render() {
if(this.props.data.length > 0) {
return (<div>
{data.map(todo => {
return <Task
id={todo.id}
key={todo.id.toString()}
title={todo.title}
onTitleChange={this.props.onTitleChange}
onTaskDelete={this.props.onTaskDelete}
/>
});}
</div>);
}
return (
<div>list empty</div>
)
}
}
export default TasksList;