Accessing JSON Array Data in React.js/Next.js Combo - reactjs

Given an API which returns JSON data like:
["posts":
{"id":1,
"name":"example",
"date":"exampledate",
"content":"examplecontent",
"author":"exampleauthor"},
{"id":2,
..]
The length of the array is unknown.
I am fetching data via isomorphic-fetch like this:
displayPosts.getInitialProps = async function() {
const res = await fetch('.../post');
const data = await res.json();
return{
posts: data.posts
}
}
which is working (console.log.stringify(data)).
Now i want to display such posts on my displayPosts page.
Therefore i am using the following React Component.
class Posts extends React.Component {
stat = {
// here i don't know how to set the state
}
render() {
return (
// i want to render the data here
);
}
}
export default Posts;
Question: How do i set a state, so that i can neatly display every post in my displayPosts.js page with
<Posts posts={props.Posts}/>
?

class Posts extends React.Component {
state = {
posts: []
}
componentDidMount() {
this.savePosts();
}
componentDidUpdate() {
this.savePosts();
}
savePosts = () => {
if(this.props.posts){
//do any other processing here first if you like
this.setState({
posts: this.props.posts
});
}
}
You probably don't need to save the posts in state, since you could just pull them from props directly. But if you need to process or transform them somehow, it might make sense.
In either case, you just hook into the lifecycle methods to do this.
Note: This will set the state every time the component updates. Often you only want to update the state when that specific prop changes. If so, you can first compare the old and new props to see if it has changed in a way that means you want to update your state.

Related

Initial State of Redux Store with JSON Data?

I am working on React app where the state is managed by redux. I am using actions.js file to fetch JSON data and store it directly in the store. The initial Store has just one key (data) in its obj with null as its value.
I use componentDidMount() Lifecycle to call the function which updates the store's data key with the JSON data I receive. However, whenever I load my app it gives an error because it finds the data value as null.
I get it. componentDidMount() executes after the app is loaded and the error doesn't let it execute. I tried using componentWillMount() but it also gives the same error. ( Which I use in JSX )
When I try to chanage the data's value from null to an empty obj it works for some level but after I use it's nested objects and arrays. I get error.
I wanna know what is the way around it. What should I set the vaue of inital State or should you use anyother lifecycle.
If your primary App component can't function properly unless the state has been loaded then I suggest moving the initialization logic up a level such that you only render your current component after the redux state has already been populated.
class version
class LoaderComponent extends React.Component {
componentDidMount() {
if ( ! this.props.isLoaded ) {
this.props.loadState();
}
}
render() {
if ( this.props.isLoaded ) {
return <YourCurrentComponent />;
} else {
return <Loading/>
}
}
}
export default connect(
state => ({
isLoaded: state.data === null,
}),
{loadState}
)(LoaderComponent);
Try something like this. The mapStateToProps subscribes to the store to see when the state is loaded and provides that info as an isLoaded prop. The loadState in mapDispatchToProps is whatever action creator your current componentDidMount is calling.
hooks version
export const LoaderComponent = () => {
const dispatch = useDispatch();
const isLoaded = useSelector(state => state.data === null);
useEffect(() => {
if (!isLoaded) {
dispatch(loadState());
}
}, [dispatch, isLoaded]);
if (isLoaded) {
return <YourCurrentComponent />;
} else {
return <Loading />
}
}
And of course you would remove the fetching actions from the componentDidMount of the current component.

How to save fetched data from server to component state using redux and redux-thunk?

In my react app I have component named profile, and I am fetching data from server and showing it inside that component. I am using redux and redux-thunk along with axios. With help of mapDispatchToProps function, i am calling redux action for fetching that data when component is mounted and saving it to redux state. After that, using mapStateToProps function i am showing that data on the screen via props. That works fine. Now I want to have possibility to edit, for example, first name of that user. To accomplish that i need to save that data to component state when data is fetched from server, and then when text field is changed, component state also needs to be changed. Don't know how to save data to component sate, immediately after it is fetched.
Simplified code:
const mapStateToProps = (state) => {
return {
user: state.user
}
}
const mapDispatchToProps = (dispatch) => {
return {
getUserData: () => dispatch(userActions.getUserData())
}
}
class Profile extends Component {
state:{
user: {}
}
componentDidMount (){
this.props.getUserData()
// when data is saved to redux state i need to save it to component state
}
editTextField = () => {
this.setState({
[e.target.id]: e.target.value
})
};
render(){
const { user } = this.props;
return(
<TextField id="firstName"
value={user.firstName}
onChange={this.editTextField}
/>
)
}
}
You can use componentDidUpdate for that or give a callback function to your action.
I will show both.
First lets see componentDidUpdate,
Here you can compare your previous data and your present data, and if there is some change, you can set your state, for example if you data is an array.
state = {
data: []
}
then inside your componentDidUpdate
componentDidUpdate(prevProps, prevState) {
if(prevProps.data.length !== this.props.data.length) {
// update your state, in your case you just need userData, so you
// can compare something like name or something else, but still
// for better equality check, you can use lodash, it will also check for objects,
this.setState({ data: this.props.data});
}
}
_.isEqual(a, b); // returns false if different
This was one solution, another solution is to pass a call back funtion to your action,
lets say you call this.props.getData()
you can do something like this
this.props.getData((data) => {
this.setState({ data });
})
here you pass your data from redux action to your state.
your redux action would be something like this.
export const getData = (done) => async dispatch => {
const data = await getSomeData(); // or api call
// when you dispatch your action, also call your done
done(data);
}
If you are using React 16.0+, you can use the static method getDerivedStateFromProps. You can read about it react docs.
Using your example:
class Profile extends Component {
// other methods here ...
static getDerivedStateFromProps(props) {
return {
user: props.user
}
}
// other methods here...
}

Can I use Redux for the real time data

I am new to ReactJS and exploring it for an application that requires real time data.
Lets say I have "n" components that have to show the real time data (1 second update). The app should consolidate all the "n" components' related "tags" and call the webserver which gives the real time data for all the "tags"( I cannot make a call from each component as it will be a huge load. So I have to consolidate and make a single call).
There is a component "RealTimeDataFetcher" which has a timer and invokes an action "GETLIVEDATA" every second. This action calls the webservice and returns the response (Using thunk). DataReducer returns the updated state and all the "n" components uses the MapStateToProps for getting the live data.
Actions
export const subscribeForData = tag =>{
return {type : 'SUBSCRIBE' , payload : tag}
}
export const getLiveData = tag[] =>{
const response = await makeServiceCall(tag[]);
dispatch({ type: 'LIVE_DATA', payload: response.data });
}
Reducer
export const liveData = (livedata, action){
// update state for live data
}
export const subscribedTags= (tags, action){
// update state for subscribed tags
}
RealTimeDataFetcher
class RealTimeDataFetcher extends React.Component{
componentDidMount(){
// call subscribeData for fetching livedata for "tags"
}
}
function mapStateToProps (state){
return { subscribedTags: state.subscribedTags};
}
connect(mapStateToProps,{...})(RealTimeDataFetcher)
Component
class SomeComponent extends React.Component{
ComponentDidMount(){
this.props.subscribeForData("tag");
}
ComponentDidUpdate(){
// I will get the live data here.
}
}
function mapStateToProps (state){
return { liveData: state.liveData};
}
connect(mapStateToProps,{...})(SomeComponent )
This approach is working. But I am not sure if this is the right way to get the real time data.
Is there any other approach that I can look into.

React/Redux Lazy load data

I have a React application which uses Redux as state container.
I have a REST API which I use to load data into the app.
Some of the data only contains IDs and to show the real content behind the IDs I need to fetch more data from the REST API.
To make an example:
I fetch a list of events. These events have speakers and participants.
When I click on an event in the app it shows the speaker and also some of the participants, but it's only a preview of them. So not all of them are shown. To show a person, with its image and a name, I need to make another API call.
My question is what is the best way to structure my code so I just need to call the API for the persons that are shown. So if I click on one event I only need to load the persons involved in this event, and also only the ones that are previewed.
I also try to have my components which render the persons not depend on the Redux store but be just plain React components which get their state from their props.
I can provide more details if needed.
You can simply load your extra data using componentDidMount make the API call and set the state locally or dispatch an async Action Creator in the componentDidMount, store the event in your redux store and retrieve it. Depends if you want to use the event detail data in other parts of your application or it is only used in that particular view, and what your personal preference is of course.
Using local state ->
class EventDetail extends React.Component {
constructor() {
super(props);
this.state = {
event: null
};
}
componentDidMount() {
// Get event id from passed prop
const id = this.props.eventId;
// Or using react-router, so you can directly link to the URL
// https://reacttraining.com/react-router/web/example/url-params
const id = this.props.match.params.eventId;
// Make API call using whatever you are using
// HTTP GET 'events/${id}' -> Details including persons
apiCallToGetEventDetailsIncludingPersons(id)
.then(event => { this.setState({ event: event }); })
.catch(err => { /* handle error */ } );
}
render() {
// Render result when data is set
// Possible to show loader when data is not yet retrieved instead of null value
return this.state.event ? (
<div>Your view</div>
) : null;
}
}
Using Redux ->
class EventDetail extends React.Component {
constructor() {
super(props);
}
componentDidMount() {
// Get event id from passed prop
const id = this.props.eventId;
// Or using react-router, so you can directly link to the URL
// https://reacttraining.com/react-router/web/example/url-params
const id = this.props.match.params.eventId;
// Fire async action creator
this.props.getEvent(id);
}
render() {
// Render result when data is set
// Possible to show loader when data is not yet retrieved instead of null value
return this.props.event ? (
<div>Your view</div>
) : null;
}
}
const mapStateToProps = (state) => ({
event: state.yourReducer.whatYouNameYourStoredEvent
});
const mapDispatchToProps = (dispatch) => ({
getEvent: (id) => dispatch(getAsyncEventDetailsAction(id))
});
const EventDetailContainer = connect(mapStateToProps, mapDispatchToProps)(EventDetail);

Best practice for making small changes in UI with React and Redux

I'm using Redux and React to load data from a web service which is working well. I'd like to make small non-webservice-based changes in the UI in response to an action. A simplified example:
class SmartComponent extends React.Component {
handleClick = (e) => {
// how to best handle a simple state change here?
}
render() {
const { displayMessage } = this.props
return (
<DumbComponent message={displayMessage}/>
<button onclick={this.handleClick}>Change Message</button>)
}
}
const mapStateToProps = state => {
// state variables linked in the reducer
}
export default connect(mapStateToProps)(SmartComponent)
let DumbComponent = ({ message }) => {
return ({message})
}
If I modify the state in SmartComponent, for instance, by using this.setState, the props of SmartComponent will not be automatically updated. I believe it's a React anti-pattern to directly modify the props of SmartComponent. Is the best way to update the message in DumbComponent to make an action creator and link it in the reducer? That seems a bit overkill for a simple message change.
Yes, you should link it to the reducer.
However this is not mandatory:
How to do it
One other way to do this would be to store the message in the state of the SmartComponent.
Beware about the fact that Redux is no longer the single source of truth for the message.
class SmartComponent extends React.Component {
constructor(props) {
super(props)
// Initialize state based on props
this.state = {
message: props.message,
}
}
componentWillReceiveProps(nextProps) {
// Handle state update on props (ie. store) update
this.setState({ message: ... })
}
handleClick = (e) => {
this.setState({ message: ... })
}
render() {
const { displayMessage } = this.state
return (
<DumbComponent message={displayMessage}/>
<button onclick={this.handleClick}>Change Message</button>)
}
}
const mapStateToProps = state => {
// state variables linked in the reducer
}
export default connect(mapStateToProps)(SmartComponent)
let DumbComponent = ({ message }) => {
return ({message})
}
Should you do it ?
If the data you display in this component can be completely isolated from the rest of your application, that is to say no dispatched action could modify it, and no other component need it, keeping this data in the store can be unnecessary.
I mostly use this method to perform optimistic updates to the view component without altering the store until new value is saved by the server.

Resources