I would like to know how to dispatch action with different params in react redux.
In format.js, queryData with different param is called by callFetch method.
After props dispatch, in the render method, will receive data in props memberData,
I receive the response memberDatawith status Inactive but
How to display for both the Active and Inactive,
// format.js
componentDidMount(){
this.callFetch();
}
callFetch(){
this.props.dispatch(queryData(this.createFetchUrl("Active")));
this.props.dispatch(queryData(this.createFetchUrl("Inactive")));
}
createFetchUrl(status) {
const body ={
position: "civil",
status: status
}
return body;
}
render(){
const { memberData } = this.props.data
return(
<div>
Active
<p>{memberData}</p>
Inactive
<p>{memberData}</p>
</div>
)
}
//actions.js
export const queryDeviceByMember = payload =>
queryApi({
request: CONSTANTS.DATA,
url: `api/details`,
encode: false,
success: CONSTANTS.DATA_SUCCESS,
failure: CONSTANTS.DATA_FAILURE,
...payload
});
//reducer.js
case CONSTANTS.DATA_SUCCESS:
case CONSTANTS.DATA_FAILURE:
return {
...state,
memberData: data,
apiPending: false,
errormsg: errormsg,
servererror: servererror || ""
};
am receiving the same response for both active and inactive states, since it overrides, without overriding how to do
[{
id: "20",
name: "xxx",
device: "ios"
},
{
id: "10",
name: "yyy",
device: "ios"
}]
You are overwriting the data in memberData, consider following these steps:
Split your reducers for active and inactive cases
Use a selector to select the data from your state, i.e:
export const getActiveMembers = state => state.activeMembers;
export const getInactiveMembers= state => state.inactiveMembers;
Import the selectors from step 2 into your components, and use mapStateToProps(state) to get the data from your state:
function mapStateToProps(state) {
return {
activeMembers: getActiveMembers(state),
subscription: getInactiveMembers(state)
};
}
Use the data in render:
render(){
const { memberData } = this.props.data
return(
<div>
Active
<p>{memberData}</p>
Inactive
<p>{memberData}</p>
</div>
)
}
Good Luck
P.S: I would use one API call to get all the members and then classify them to inactive & active in my state using the reducer.
Related
I have a task of adding implementation on a given website
There is a function of getting data (tickets) asynchronously and I need to allow a sorting option.
The sorting happens on the server side, and by a button click (of sorting type), the sorted data should be fetched and displayed again to the user.
My idea is this:
Get the sorted data on button click
Use setState() to set the new sorting type
This will call componentDidUpdate() and it will setState() to the new data asynchronously.
The last setState() should call render to display the data again
Everything works except for part 4 - The render function is being called but the data is not updated on the screen.
The class I am working on is this:
App.tsx
export type AppState = {
tickets?: Ticket[];
search: string;
sortBy: string;
};
export class App extends React.PureComponent<{}, AppState> {
state: AppState = {
search: "",
sortBy: "none",
};
async componentDidMount() {
this.setState({
tickets: await api.getTickets(this.state.sortBy),
});
}
renderTickets = (tickets: Ticket[]) => {
/*
* more stuff here
*/
// log that verifies I am getting the sorted data
console.log("Tickets in rednerTickets: ")
{tickets.map((t)=>{
console.log(t);
})}
return (
<ul className="tickets" id="all_tickets">
{tickets.map((ticket) => (
<CustomTicket t={ticket}></CustomTicket>
))}
</ul>
);
};
async componentDidUpdate(prevProp: AppState, prevState: AppState) {
// if the sortBy state is different
if (prevState.sortBy !== this.state.sortBy) {
// perform getTickets and set the new tickets
this.setState({
tickets: await api.getTickets(this.state.sortBy),
});
}
}
render() {
const { tickets } = this.state;
console.log("tickets in render: ", tickets);
return (
<main>
<h1>Tickets List</h1>
<button
onClick={() => {
this.setState({ sortBy: "date" });
}}>Sort By Date
</button>
<button
onClick={() => {
this.setState({ sortBy: "title" });
}}>Sort By Title
</button>
<button
onClick={() => {
this.setState({ sortBy: "email" });
}}>Sort By Email
</button>
{/* Calls the function that renders the tickets */}
{tickets ? this.renderTickets(tickets) : <h2>Loading..</h2>}
</main>
);
}
}
export default App;
CustomTicket is another class component that gets ticket's data and displays it.
Note:
I tried to change componentDidUpdate to this:
async componentDidUpdate(prevProp: AppState, prevState: AppState) {
// if the sortBy state is different
if (prevState.sortBy !== this.state.sortBy) {
// perform getTickets and set the new tickets
this.setState({
tickets: await api.getTickets(this.state.sortBy),
});
// new line:
ReactDOM.render(this.render(),document.getElementById('root'));
}
}
But it worked only for the first click, and it also came with "can't perform a react state update on an unmounted component" warning
Try this:
async componentDidUpdate(prevProp: AppState, prevState: AppState) {
if (prevState.sortBy !== this.state.sortBy) {
const tickets = await api.getTickets(this.state.sortBy)
this.setState({
tickets
});
}
}
In your implementation setState is calling and trying to take tickets, but tickets it is a Promise - not needed data.
First of first need to get tickets and then to call setState
I am trying to set the state with data received from a fetch request in the parent component. I am receiving an array of objects that each have the following keys: ‘name’, ‘artist’, ‘url’, ‘cover’, ‘lrc’, and ‘theme’. I am using Object.keys() to map over the object data, but I am wondering how I can set the state in this way so as to have multiple objects with those six keys be stored in the state so my state will look like:
this.state = { data: [{ {…}, {…}, {…}, etc… }] }
One big issue is that my data - from a fetch request in the parent - is not rendering in this tempComp component. I am passing the data in as a prop (this.props.playlist). Why is the fetched data in the parent not rendering in the tempComp component, and how can I set state with multiple objects, as I attempted below with Object.keys()? Any advice is greatly appreciated.
import React, { Component } from 'react'
class tempComp extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
audio: [{
name: '',
artist: '',
url: '',
cover: '',
lrc: '',
theme: ''
}]
}
}
async componentDidMount() {
console.log('playlist in componentDidMount()', this.props.playlist) //<--- AudioPlayer data should be coming in here
var json = this.props.playlist;
var arr = [];
Object.keys(json).forEach(function (key) {
arr.push(json[key]);
});
arr.map(item => {
this.setState({
audio: [{
name: item.name,
artist: item.artist,
url: item.url,
cover: item.cover,
lrc: item.lrc,
theme: item.theme
}]
})
})
console.log(this.state.audio);
}
render() {
return (
<div>
</div>
)
}
}
export default tempComp
And here is the parent component, for clarification:
export default class PostContent extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
episodeData: [],
}
}
async componentDidMount(){
const { id } = this.props.match.params;
const response = await fetch(`http://localhost:5000/episode/${id}/playlist`);
const jsonData = await response.json();
this.setState({
episodeData: jsonData, //this is working!...
id: id
});
console.log(this.state.episodeData) //...see?
}
render() {
return (
<Fragment>
<TempComp playlist={this.state.episodeData} />
<AudioPlayer playlist={this.state.episodeData} />
<Link id='home-link' to='/' activeClassName='active'>Homepage</Link>
{this.state.episodeData.map((item, i) => (
<div key={i} className="word-content">
<h2 className="show-title">{item.post_title}</h2>
<div className="episode-post-content">{item.post_content}</div>
</div>
))}
<Table data={this.state.data} />
<div className="bottom-link">
<Link id='home-link' to='/' activeClassName='active'>Homepage</Link>
</div>
</Fragment>
)
}
}
You've been a bit tricked here with the way setState works. It's an asynchronous method which means it's not going to run in tandem with your map. You could use a setTimeout or an await which I can see is possible given your componentDidMount is prefixed by async. However, if I can take the liberty, i'd recommend you make some small changes like so:
const json = {
apple: { yum: false },
peach: { yum: true }
};
const yummyness = Object.keys(json).map(key => {
return { yum: json[key].yum }
});
// yumminess will render: [{ yum: false }, { yum: true }]
this.setState({ yummyness });
What I've done here is a few things:
If data isn't changing, assign it to a const, you can learn more about that here
Map returns an array. This can be really handy such as now, so instead of pushing to your arr value, I've just returned an object to prevent you doing your second map.
Finally, as I mentioned earlier setState is asynchronous and a bit of a slippery sucker! So to avoid that I'm just letting the map do it's thing and THEN I've assigned it to yummyness.
Bonus Round: I've used fruit but I've left most of your naming the same. json is your props.playlist and yumminess is your arr
Hope this helped!
My problem is that the code is working correctly
I would like to be able to change the value val: 'yolo' by either a component from another page or direct by my database
Do you have an idea, how to fix this? Neff
import React from 'react'
import axios from 'axios'
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT + '/api';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
this.clickHandler = this.clickHandler.bind(this)
this.state = {currentPosition: 0, totalLength: 3, val: 'yolo'}
}
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
clickHandler(){
this.setState({currentPosition: (this.state.currentPosition + 1)%this.state.totalLength})
}
render() {
return (
<div >
<button onClick={this.clickHandler} >Move to the Right</button>
{
Array.from(
{length: this.state.totalLength},
(_,i) => (
<div key={i} className="slot">
<p>{i === this.state.currentPosition ? this.state.val : null}</p>
</div>
)
)
}
</div>
)}
}
export default App;
one way you can change the value of Yolo similar way as you are getting data from the server.
as for changing it from another component , you can do it by either getting it as a props from its parent component where you use this component
<App yoloVal = {"yoloValue"}/>
and you can receive it in props either when it mounts or when it updates
componentDidMount(){
this.setState({
yolo : this.props.yoloVal
}
}
or when it updates
componentDidUpdate(){
if(this.props.yoloVal !== prevProps.yoloVal){
this.setState({
yolo : this.props.yoloVal
}
}
}
you can also get this value from a child in the App by passing it a method
write a method in the App Component
setYoloValue(val){
this.setState({
yolo : val
}
}
now pass this method in render method of App to a child component
return (
<ChildComponent setYoloValue = {this.setYoloValue.bind(this)}
)
we are using bind so when this method is called the context remains of the parent instead of the caller(child component)
now you can use this method anywhere in the child to set the value of Yolo on parent
class ChildComponent extends Component {
componentDidMount(){
this.props.setYoloValue("new Yolo Value by child")
}
}
Now as for passing data between siblings , you can give the data by using the above two methods , first have a common parent , pass the data to parent by using second method then pass that data parent received to the other children as the first method. that is how you can acheive communication between siblings components.
as for setting the value from any other component in the app that is not directly related to you component , you need Redux or similar that does the job for you by keeping the values in a common store and components listen to that store and receive the update when the value in the store updates.
I would like to be able to change the value val: 'yolo'
1.by either a component,
2.from another page
3.or direct by my database
i'm actually surprised by the following piece of code, and not even sure, it 's a valid one. you are initializing this.state twice inside your constructor.
constructor(props) {
super(props);
--> this.state = {
data: [],
};
this.clickHandler = this.clickHandler.bind(this)
--> this.state = {currentPosition: 0, totalLength: 3, val: 'yolo'}
}
you initialize your entire variables inside your constructor..
constructor(props) {
super(props);
this.state = {
data: [],
currentPosition: 0,
totalLength: 3,
val: 'yolo',
};
this.clickHandler = this.clickHandler.bind(this)
}
idea is to pass a function(prevState) as a callback to update the local state so as to escape batching.
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState(prevState => ({
...prevState,
data: res.data,
}))
}
i'm not sure this will work as you expected..
clickHandler(){
this.setState({currentPosition: (this.state.currentPosition + 1)%this.state.totalLength})
}
since you are doing a division, it's good to Math.floor or ceil(you need to find whichever value meets your requirement.)
//1. by a component..
handleValChange(val) => {
this.setState(prevState => ({
...prevState,
val,
}))
}
//now u can pass it to a child component.
render() {
const { handleValChange } = this
return (
<div>
<...rest of the div.../>
<ChildComponent {...{ handleValChange }} />
</div>
)
}
from another page.
from another page means, probable a diffrent route. in such cases u need to update this globally(redux, mobx etc..) and the value should also live globally not locally. u can pass id's and stuff via url but function, not possible.
direct by db.
this is where u make an api call and based on the response u update the state. that means, it's time to extract your application into a global state(redux, mobx etc..)
this.state = {
data: [],
};
this.clickHandler = this.clickHandler.bind(this)
this.state = {currentPosition: 0, totalLength: 3, val: 'yolo'}
You should not have two states in one constructor. Change it to one state:
this.state {
data: [],
currentPosition: 0,
totalLength: 3,
val: 'yolo',
}
As for changing the value from another component, there are two easy solutions.
1) Using Redux to handle state, instead of local state, probably the best solution.
2) Use a callback function that call setState in that component, and pass it to the other component, if it is a child of this component.
const myCallbackFunction(value: string) {
this.setState({ val: value })
}
I have a component Home that is called after user get in. There are some data in that screen, and in the header I have a icon button that send the user for a screen where the user can see their profile data and also delete account. So when the icon button is clicked I'm sending data using props.navigation, sending the user to another screen/component.
profile = () => {
const { navigation } = this.props;
const user = navigation.getParam('user', 'erro');
this.props.navigation.navigate('profileScreen', { user: user });
}
Inside the new component, I tried to setState inside the method componentDidMount using that data but it didn't work. I checked using console.log the data is there. How could I setState in this case?
export default class profileScreen extends Component {
static navigationOptions = {
title: "Profile"
};
constructor(props) {
super(props);
this.state = {
user: {}
}
}
componentDidMount() {
const {navigation} = this.props;
const user = navigation.getParam('user', 'Erro2');
this.setState({user: user.user});
console.log(this.state); // Object {"user": Object {},}
console.log("const user");
console.log(user.user); //my actual data is printed
}
render() {
return (
<Text>{this.state.use.name}</Text>
<Text>{this.state.use.age}</Text>
<Text>{this.state.use.email}</Text>
...
...
)
}
}
Result from console.log(this.state)
Object {
"user": Object {},
}
Result from console.log(user)
Object {
"createdAt": "2019-04-27T21:21:36.000Z",
"email": "sd#sd",
"type": "Admin",
"updatedAt": "2019-04-27T21:21:36.000Z",
...
...
}
It seems like you're trying to send an object (user) as a route parameter with react-navigation library. It's not possible.
The proper way of doing such scenarios is to send the user's id userId as route parameter and load user details from your API (or state).
profile = () => {
const user = {id: 10 /*, ... rest of properties */}; // or take it from your state / api
this.props.navigation.navigate('profileScreen', { userId: user.id });
}
componentDidMount() {
const {navigation} = this.props;
const userId = navigation.getParam('userId', 'Erro2');
// const user = read user details from state / api by providing her id
this.setState({user: user});
}
ps: if you are using state management like redux/flux/..., consider setting currentUser in your global state and read that instead of passing userId as a route param.
To make sure component updates when the user got new value in the state render method should be like this:
render() {
const {user} = this.state
return (
<View>
{user && <Text>{user.name}</Text>}
{user && <Text>{user.age}</Text>}
{user && <Text>{user.email}</Text>}
...
...
</View>
)
}
Note 0: const {user} = this.state would save you from repeating this.state
Note 1: it would much more elegant to wrap all those <Text> component inside another <View> to prevent repeating the condition phrase {user && ...}
I am using Redux thunk and axios to make server calls and modify the state depending on the result.
The problem is that when I use a connected component, and its initial state depends on data from the server, it does not render (the connected props are empty)
render () (<div>{this.props.someData}</data>) // empty, or error, if nested
...
const mapStateToProps = state => ({
someData: state.someData
})
I also tried this:
componentWillMount = () => {
this.setState({
someData: this.props.someData
})
}
And used state in render, but it didn't help.
Is there a way to wait for the server response before rendering or some other solution?
You can conditionally render that part. Use a property to indicate fetching status (property name is loading in my example).
class UserDetails extends React.Component {
state = {
loading: true,
data: null
}
fetch() {
this.setState({
loading: true
})
// an axios call in your case
setTimeout(() => this.setState({
loading: false,
data: {
nestedValue: 'nested value'
}
}), 500)
}
componentDidMount() {
this.fetch()
}
render() {
return <div>
{this.state.loading ? <span>loading...</span> : <div>nested prop value: {this.state.data.nestedValue}</div>}
</div>
}
}
Typically you would use the Component.defaultProps object to initialize your props. So in your case, something like this will set your someData prop to an initial value before axios receives the data.
class MyComponent extends React.Component {
// ... your implementation
}
MyComponent.defaultProps = {
someData: [] // empty array or whatever the data type is
};
Edit: docs