I have tried numerous ways to set state but for some reason, the state never gets updated.this is the JSON data that I want my state to change to
export class Provider extends Component {
state = {
posts: [],
profileinfo: {},
dispatch: action => this.setState(state => reducer(state, action))
};
componentDidMount() {
fetch("http://localhost:3001/login").then(response =>
response
.json()
.then(data => this.setState({ profileinfo: data.firstname }))
);
console.log(this.state.profileinfo);
}
render() {
// ...
}
}
setState is asynchronous. Your console log probably triggers before the state got updated. If you want to see the result after the setState call, do it this way:
data => this.setState({ profileinfo: data.firstname }, () => {
console.log(this.state);
});
Related
I'm new to the react.
I have this state :
state = {
isLoading: true,
};
And I have this lifecycle function
componentDidMount() {
const { setPageCount, receiveApiData } = this.props.actions;
const { isLoading } = this.state;
const getData = () => {
this.setState({ isLoading: !isLoading });
receiveApiData();
setPageCount();
};
setInterval(() => {
getData();
}, 30000);
}
Here is what I'm trying to return in render():
return isLoading ? (
<Loading></Loading>
) : ( `Some Code here`)
The problem is state is Always true and my lifecycle method is not changing it to the false so my app can not render the false condition.
I don't know what to do,any suggestions please?
Everything else in getData() is working correctly
The issue is here:
this.setState({ isLoading: !isLoading });
because isLoading what you are destructuring taking previous value i.e it is not taking latest state value so its not updating your isLoading . What you need to do is this:
this.setState({ isLoading: !this.state.isLoading });
Here is the demo: https://codesandbox.io/s/interesting-chandrasekhar-pluv7?file=/src/App.js:312-332
Since your new state depends on the value of your old state, you should use the functional form of setState
this.setState(prevState => ({
isLoading: !prevState.isLoading
}));
Official documentation: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
Consider this HOC I use for fetching data
function withData(Component, endpoint) {
return class extends React.Component {
state = {
result: null,
loading: false,
error: { state: false, msg: '' }
};
fetchData = async () => {
try {
this.setState({ loading: true });
const response = await axios.get(`${endpoint}/${this.props.params || ''}`);
this.setState(state => ({ result: response.data, loading: !state.loading }));
} catch (err) {
console.log('Error caugh in withData HOC', err);
this.setState({ error: true });
}
};
componentDidMount() {
this.fetchData();
}
componentDidUpdate(prevProps) {
if (this.props.params !== prevProps.params) {
this.fetchData();
}
}
render() {
const { result, loading } = this.state;
if (loading) return <p>Loading...</p>;
if (!result) return null;
return <Component result={result} {...this.props} />;
}
};
}
You will notice I am saying if !result do not render the component. The problem is when this.props.params to this component changes, this.state.result is preserving the value of the older state. I want to reset result to null after each render, so it behaves the exact same as the initial render.
How can I achieve this?
To make it more clear, it would be great if I could do this in componentWillUnmount so that it's ready for the next Component lifecycle. However the component never unmounts.
Please note, it must be done in the HOC and not in the Component it returns.
In this case you'll want your component (rendered by the HOC) to accept a key option, which will be the params props in my case.
Usually you use those for lists, but it can be used here as well. When a key is used, if it changes instead of updating, it will make a new instance of the component. That would mean you would not need the componentDidUpdate anymore.
You can read more about the bahavior here https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
Trying to understand why the component's state is becoming undefined.
Before the async call the console shows this.state.pubsubtopics as [], after the call it becomes undefined
code:
class PubSubTopics extends React.Component{
constructor(props){
super(props);
this.state = {
pubsubtopics: [],
};
}
componentDidMount() {
console.log('after component mounted');
console.log(this.state.pubsubtopics);
this.callBackEndAPI()
.then(res =>
this.setState({pubsubtopics: res.express}))
.catch(err => console.log(err));
console.log('after setting state');
console.log(this.state.pubsubtopics);
}
callBackEndAPI = async () => {
const response = await fetch('/listtopics');
const body = await response.json();
if(response.status !== 200){
throw Error(body.message)
}
return body;
}
handlePrintStateClick = () => {
console.log(this.state.pubsubtopics);
}
render(){
return(
<div>
<ul>
</ul>
<button onClick={this.handlePrintStateClick}>printState</button>
</div>
)
}
}
Logs (last log entry is from clicking the button):
after component mounted
index.js:16 []
index.js:21 after setting state
index.js:22 []
index.js:36 undefined
res.express didn't exist in the server's response, using res.topics solved the problem
I have this class component and want to rewrite it to stateless functional component with recompose:
export default class Popular extends Component {
state = {
value: 0,
selected: "All",
repos: null
}
componentDidMount() {
this.handleSelected(this.state.selected)
}
handleChange = (e, value) => {
this.setState({ value })
}
handleSelected = lang => {
this.setState({
selected: lang,
repos: null
})
fetchPopularRepos(lang).then(repos => {
this.setState({
selected: lang,
repos
})
})
}
I'm struggling to combine onSelectLanguage and onFetchRepos in one function as in my code before refactoring. I don't know how to rewrite this for my componentDidMount function as well.
UPDATE:
got this working with:
const enhance = compose(
withStateHandlers(initialState, {
onChangeLanguage,
onSelectLanguage
}),
lifecycle({
componentDidMount() {
fetchPopularRepos(this.props.selected).then(repos => {
this.setState({
repos
})
})
}
}),
lifecycle({
componentDidUpdate(prevProps) {
if (this.props.selected !== prevProps.selected) {
this.setState({ repos: null })
fetchPopularRepos(this.props.selected).then(repos => {
this.setState({
repos
})
})
}
}
})
)
These lifecycles don't look very sexy though. Not sure if this worth refactoring.
This looks like a case where you'd want to use the lifecycle() method. I'm not a recompose expert, however I think the following adjustments might achieve what you're after:
const onFetchPopularRepos = props => () => {
// Make sure the method returns the promise
return fetchPopularRepos(props.selected).then(repos => ({
repos: repos
}))
}
const withPopularReposState = withStateHandlers(initialState, {
onChangeLanguage,
onSelectLanguage
})
// Add a life cycle hook to fetch data on mount
const withLifecycle = lifecycle({
componentDidMount() {
// Update the state
onFetchPopularRepos(this.props).then(newState => this.setState(newState))
}
})();
// Compose the functional component with both lifecycle HOC
const enhance = withLifecycle(withPopularReposState)
Extending on the previous answer, you could use functional composition to combine the onFetchRepos and onSelectLanguage as required.
If I understand your requirements correctly, you should be able to achieve this by the following:
const initialState = {
value: 0,
selected: "All",
repos: null
}
const onChangeLanguage = props => (event, value) => ({
value
})
const onSelectLanguage = props => lang => ({
selected: lang
})
const onFetchRepos = props => (fetchPopularRepos(props.selected).then(repos => ({
repos
})))
// Combined function: onFetchRepos followed by call to onSelectLanguage
const onFetchReposWithSelectLanguage = props => onFetchRepos(props)
.then(response => props.onSelectLanguage(response))
// Minimal code to compose a functional component with both state handers and
// lifecycle handlers
const enhance = compose(
withStateHandlers(initialState, {
onChangeLanguage,
onSelectLanguage
}),
lifecycle({
componentDidMount() {
// Fetch repos and select lang on mount
onFetchReposWithSelectLanguage(this.props)
}
})
)
Update
// Minimal code to compose a functional component with both state handers and
// lifecycle handlers
const enhance = compose(
withStateHandlers(initialState, {
onChangeLanguage,
onSelectLanguage,
setRepos
}),
lifecycle({
componentDidMount() {
// Reuse onSelectLanguage logic and call setState manually, use setState callback that
// fires after state is updated to trigger fetch based on newState.selected language
this.setState(onSelectLanguage(this.props.selected)(this.props), newState => {
fetchPopularRepos(newState.selected).then(repos => {
this.setState({
repos
})
})
})
}
})
)
Hope this helps you!
My parent component is:
constructor(){
super();
this.state = {data: []}
}
componentWillMount(){
this.props.getData(); //makes api call
this.setState({data: this.props.data}); //get data stored in the store
}
render(){
const columns ={ //columns here}
return(
<Tables
dataSource = {this.state.data}
columns = {this.columns}
/>
)
}
const mapStateToProps = state => ({
data: state.data,
})
const mapDispatchToProps = dispatch => ({
getData: () => dispatch(getData()),
})
I do get data when I console it, but when I try to set state of data with the data props. It says data.slice is not a function.
//saga
export function* getData(action){
try{
const data = yield call(axios.get, 'http://localhost:4000/data');
console.log(data);
if (data) {
yield put(updateData(data.data));
}catch(e){
console.log(e);
}
}
//actions
export const updateData = data => ({
type: UPDATE_DATA,
data
});
//reducers
export const INITIAL_STATE = Immutable({});
export const updateData = (state, action) => {
console.log('reducers', action.data);
return { ...state, ...action.data };
};
const ACTION_HANDLERS = {
[UPDATE_DATA]: updateData,
};
export default createReducer(INITIAL_STATE, ACTION_HANDLERS);
I do not know what I am doing wrong while setting the state. I cannot seem to find out the errors.
Please help me
this.setState({data: this.props.data}); //get data stored in the store
The data is not yet there. If you really wants to set the state to use the prop data, you need to update your state in your componentWillReceiveProps(nextProps) lifecycle function, not on componentDidMount().
Analyzing this function
componentWillMount(){
this.props.getData(); //makes api call
this.setState({data: this.props.data}); //get data stored in the store
}
when you call getData(), the browser will do its thing and your redux state will be updated eventually, so, when the second line is called, the data is not yet there.
You could use some promises and/or async/await functions, but you don't really need to. You can use the regular component lifecycle functions and, event better, you can use this.props.data and not have an internal state at all.