I am working on a nextjs project where i have a helpers folder in level with pages folder.
I have a ts file inside helpers folder and here i want to get the latest state and update state depending on the latest state
This is how im getting the state
store().getState()
where store is imported from store.js
Im updating state depending on the previous state
const state = store().getState()
if(!state.currentUser){ // here im checking if state has currentUser
store().dispatch(Action) // here im calling action which will update the state
}
do further operations
The problem here is I'm not getting the updated state from store().getState() after updating the state. Is the way I'm managing things correctly? How to get the updated state?
*EDIT* : Im sending a helper function as a prop to many if my page components. Now that i dont want to touch this , i somehow want to get the updated state and dispatch actions based on the state itself. Note that the hepler function is not a functional component
Thanks in advance
The problem is that this store you're using isn't part of React, so React doesn't know when the data changes. You have to create a way to let React know that the data changes so it could then rerender your component or trigger an action.
Does your store offer a way to subscribe to changes? If so you can do something like this in your component (assuming you're using hooks):
Edit: Reusable hook way:
export const useStore = () => {
const [storeState, setStoreState] = useState(store().getState());
useEffect(() => {
const subscribeFunc = (newState) => setStoreState(newState));
store().subscribe(subscribeFunc);
return () => {
store().unsubscribe(subscribeFunc);
}
}, [])
return [storeState, store().dispatch]
}
then in your component
const [storeState, dispatch] = useStore();
// listen to changes of the currentUser and fire actions accordingly
useEffect(() => {
if (!storeState.currentUser) {
dispatch(Action)
}
}, [storeState.currentUser])
Initial way:
// sync the store state with React state
const [storeState, setStoreState] = useState(store().getState());
useEffect(() => {
const subscribeFunc = (newState) => setStoreState(newState));
store().subscribe(subscribeFunc);
return () => {
store().unsubscribe(subscribeFunc);
}
}, [])
// listen to changes of the currentUser and fire actions accordingly
useEffect(() => {
if (!storeState.currentUser) {
store().dispatch(Action)
}
}, [storeState.currentUser])
By setting the state in the component on change, React now knows that data changed and will act accordingly.
This is a very local approach to explain the concept, but it would obviously be better to create a reusable hook to use throughout your app for any store.
Related
One of the journeys in my app kicks off several consecutive dispatches, one main dispatch with several side effects based on the result of the preceding one - with each API call making a Redux state change via the reducer.
After the API calls I am feeding data to a separate microservice to bring back logic that will dictate the sub-component to render. And it is in these sub-components that I am wanting to make a single datalayer push.
The issue I am having is that I am getting multiple renders/rerenders due to the constant data changes each time the reducer is hit, as you would imagine... And each time the main parent component is rendered/rerendered due to state change, I am sending a datalayer push as the sub-component is rendered again...
I'm wondering if there are any ways in which I can stop the rendering so much and the constant triggering of my sub-component and its datalayer push.
Note - I have tried wrapping these components with React.memo and using a custom prop checker using lodash.isEqual, however the Redux state changes slightly after each reducer call, so this doesn't really help.
SubComponent.jsx
const SubComponent = props => {
useEffect(() => {
// Do datalayer push here
}, []); // useEffect runs once on render
// Return html here
}
MyComponent.jsx
const mapStateToProps = state => ({...});
const mapDispatchToProps = dispatch => ({...});
const MyComponent = (props) => {
// Note: Runs on each render - Will be required to run when any redux state changes
useEffect(() => {
// Set up microservice wizard config
});
return (
<div>
{microserviceWizard.renderPage(props.step)}
<EcommerceHandler /> // This also makes Redux state changes
</div>
);
}
I am trying to build an ecommerce website, and I hit a problem I cannot seem to resolve. I am very new to react and JS so have some patience please :)
I declared 4 useStates in my app.js:
const [elementeDinState, setElementeDinState] = useState([]);
const [currentCategorie, setCurrentCategorie] = useState("Acasa");
const [subCategorie, setSubcategorie] = useState([]);
const [cartContents, setCartContents] = useState([]);
const fetchData = useCallback(async () => {
const data = await getCategories();
setElementeDinState(data);
}, []);
useEffect(() => {
fetchData().catch(console.error);
}, [fetchData]);
const changeHeader = (dataFromMenuItem) => {
setCurrentCategorie(dataFromMenuItem);
};
const changeCopiiContent = (data1FromThere) => {
setSubcategorie(data1FromThere);
};
const changeCart = (dataFromCart) => {
setCartContents(dataFromCart);
};
I am passing the functions to change those states to different child components as props. my problem is, when I add items to cart it triggers a re render of my component (products listing component) that should not be affected by cartContents and that resets the state of said component to the initial value that changes the items being shown. does useState hook create a single global state comprised of all those states?
If these useState are defined in the app.js and then passed down, when a child will use them chasing the state will happen in the app.js so all the children of <App /> will be re-rendered.
I guess that your app.js looks similar:
function App() {
const [elementeDinState, setElementeDinState] = useState([]);
// ...and the other hooks and methods
return (
<cartContents setElementDinState={setElementeDinState} />
<ProductList />
)
}
In this case the state is in the component so when <CartContents /> changes it, it will trigger a re-render of the and all its children <ProductList /> included.
To avoid this problem think better when each piece of state needs to be and put the state as near as possibile to that component. For example, if the state of the cart does not influence the Product list. Move the useState in the <Cart /> component.
From what I understand, your problem is that you're simply resetting the cartContents state every time you call the changeCart function, correct?
What you probably want, is to add (or remove ?) the item to the cart, like this?
const changeCart = (dataFromCart) => {
setCartContents(oldContents => [...oldContents, dataFromCart]);
};
Here is a description of useState from the oficial site:
useState is a Hook (...). We call it inside a function component to add some local state to it
So it creates just a local state.
About your problem, We need more information, but I believe that some parent component of that widget is trying to render other component instead of your the component that you wanted (let's call it "ProblemComponent") and rendering you ProblemComponent from scratch again, before you can see it.
it's something like that:
function ParentComponent(props: any) {
const isLoading = useState(false);
// Some logic...
if(isLoading) {
return <LoadingComponent/>;
}
return <ProblemComponent/>;
}
If that doesn't work you can also try to use React.memo() to prevent the ProblemComponent to update when it props change.
well, seems like I wanted to change the way react works so I figured out a work around, based on what you guys told me. I declared the state of the productsComponent in the parent component and adding to cart now doesn't force a refresh of the items being shown. thank you!
There is an event handler for click and when it triggered i want to pull specific data from redux using selector where all logic many-to-many is implemented. I need to pass id to it in order to receive its individual data. Based on rules of the react the hooks can be called in function that is neither a React function component nor a custom React Hook function.
So what is the way to solve my problem ?
const handleMediaItemClick = (media: any): void => {
// For example i check media type and use this selector to pull redux data by id
const data = useSelector(playlistWithMediaSelector(imedia.id));
};
As stated in the error message, you cannot call hooks inside functions. You call a hook inside a functional component and use that value inside the function. The useSelector hook updates the variable each time the state changes and renders that component.
Also, when you get data with useSelector, you should write the reducer name you need from the redux state.
const CustomComponent = () => {
// The data will be updated on each state change and the component will be rendered
const data = useSelector((state) => state.REDUCER_NAME);
const handleMediaItemClick = () => {
console.log(data);
};
}
You can check this page for more information.https://react-redux.js.org/api/hooks#useselector
You should probably use local state value to track that.
const Component = () => {
const [imediaId, setImediaId] = useState(null);
const data = useSelector(playlistWithMediaSelector(imediaId));
function handleMediaClick(id) {
setImediaId(id)
}
useEffect(() => {
// do something on data
}, [imediaId, data])
return <div>...</div>
}
Does that help?
EDIT: I gather that what you want to do is to be able to call the selector where you need. Something like (considering the code above) data(id) in handleMediaClick. I'd bet you gotta return a curried function from useSelector, rather than value. Then you would call it. Alas, I haven't figured out how to that, if it's at all possible and whether it's an acceptable pattern or not.
I have an application that has some complex data fetching. Overall, here is a snapshot of the logic in my application
// dep1 is from redux, dep2 is local state
// useEffect 1
useEffect(() => {
// perform some state variable update to dep2
}, [dep1]);
// useEffect 2
useEffect(() => {
// use some values from deps to fetch data
}, [dep1, dep2]);
The issue I am facing is that when dep1 and/or dep2 update, the state change from useEffect 1 needs to reflect in the request url of the data fetching operation in useEffect 2. useEffect 2 ends up running twice, once with the dep1 update (without the dep2 update from useEffect 1 in the url) and once with the dep2 update. This issue is not specifically noticeable in most cases where we are just rendering, but we end up with double api fetches in cases where data fetching is used in the useEffect. What is some strategy that I can use to circumvent this double API call?
EDIT
Adding more code to allow more specifity for problem:
// useEffect 1
// when the user is changed (user is a prop that is from redux),
// option should be reset to "DEFAULT"
useEffect(() => {
setOption("DEFAULT");
}, [currentUser]);
// useEffect 2
// option is a value that can be set within the UI and is local state.
// setting option to a new value will trigger api call with new value
useEffect(() => {
const data = await getData(option);
}, [currentUser, option]);
The issue when option is not "DEFAULT" and currentUser changes, useEffect 2 will run twice. I would like to find some logic to allow it to run once with option set back to "DEFAULT" if currentUser changed. Is this possible using other react patterns, since it doesn't seem possible with useEffect?
I think you are complicating it alot. I would suggest using a single effect hook and compute the logic and perform data fetching in the same effect.
/ dep1 is from redux, dep2 is local state
// useEffect 1
useEffect(() => {
// perform some state variable update to dep2
// perform data fetching here and if you want to check some condition for dep2 you could do that here as well.
}, [dep1]);
Try setting dep2 inside a react component within the current component and passing it as a prop, so that you only have one useEffect in each component, with state being passed up.
const component = () => {
useEffect(() => {
// do something
}, [dep1]);
return (
<div> <ComponentTwo depTwo={depTwo}/> </div>
)
}
then in ComponentTwo ...
const componentTwo = ({ depTwo }) => {
// useEffect 2
useEffect(() => {
// use some values from deps to fetch data
}, [dep2]);
return (<div> something </div>
)
}
You'll need to import ComponentTwo inside the parent component.
This is my component's class:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import Button from '../UI/Button/Button';
import * as actions from '../../store/actions';
class Password extends Component {
submitPassword=(e)=>{
e.preventDefault();
this.props.submitPassword(this.state.password, this.props.levelNumber);
}
render() {
<Button clicked={this.submitPassword} >
Submit password
</Button>
}
}
const mapStateToProps = state => {
return {
};
}
const mapDispatchToProps = dispatch => {
return {
submitPassword: (password,levelNumber) => dispatch(actions.submitPassword(password,levelNumber))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Password);
and this is my action:
export const submitPassword = () => {
// HERE ALL MY LOGIC !!!
return {
level:undefined,
type:actions.PASSWORD
}
}
The code working all correctly including params and logic.
I wanna that every time that finish to execute the function submitPassword A third component refresh/reload with the new props. Attention! My third component is parent, not child!
It's possibile to send a command from action to component? How can I do it? I have already tried with:
componentWillReceiveProps() {
console.log("new props");
}
in my component but he can not take the event.
normally a structure my redux store as follows
{
entities: {},
ui: {},
domain:{}
}
so for example when you execute submitPassword you execute all the logic and depending on the result you can dispatch another action to update the ui or the domain part so the componentes that are connected respond to those changes.
The UI holds information about UI changes, like if you are submiting info display a progress bar or if it has to display a dialog.
The domain part holds information related to the whole webapp, like loggin info and notifications.
You don't need always to pass new props for redux state to be accessed.
As redux is having immutable state, you'll always be getting new updated state no matter the previous one. So this will enforce your component to update props to get latest state from redux. This is done by <Provider> wrapper attached on root level.
So hence your props will be having new values whenever redux state gets updated.
The lifecycle you are looking is static getderivedstatefromprops(). This lifecycle gets executed whenever props are changed/updated.
I made visual implementation on fly that can aid you the picture. Here Redux State means Redux Store