React props not passing correctly - reactjs

I am trying to pass an id value (listId) from my store as props to my Component without any success. I think the problem is that the id is generated by the addList action, which is dispatched first, followed by the addTrack action. Neither listId={props.listId} or something like listId={props.listId ? props.listId : 1234} seem to work. Here is my code:
const MakeList = (props) => {
return (
<div>
<h1>Make a list </h1>
<ListForm
listId={props.listId}
onSubmit={(item) => {
props.dispatch(addList(item));
}}
trackSubmit={(item) => {
props.dispatch(addTrack(item, props.listId));
}}
/>
</div>
)
};
const mapStateToProps = (state) => {
return {
listId: state.userLists.listIdArray[index],
};
};
export default connect(mapStateToProps)(MakeList);

Related

Passing data from child component

const Location = (props) => {
const [province, setProvince] = useState(null);
const [discrict, setDistrict] = useState(null);
const [discrictList, setdiscrictList] = useState([]);
const handleProvinceChange = (obj, e) => {
setProvince(obj);
setdiscrictList(obj.ilce);
setDistrict(null);
};
const handleDistrictChange = (obj) => {
setDistrict(obj);
};
return (
<div style={{ width: 400, marginBottom: 20 }}>
<Select
placeholder="Şehri Seçiniz"
value={province}
options={il}
onChange={handleProvinceChange}
getOptionLabel={x => x.il}
getOptionValue={x => x.il}
/>
<Select
placeholder="İlçeyi Seçiniz"
value={discrict}
options={discrictList}
onChange={handleDistrictChange}
getOptionLabel={x => x.ilce}
getOptionValue={x => x.il}
/>
</div>
)
}
export default Location;
I wanna send discrict data from this code to this component to use a function from here. Location component is rendered in List Component,List Component is in Content.
class Content extends React.Component {
state = {
locations: [],
allLocations: [],
query: "",
};
componentDidMount() {
console.log("DATA");
/*I wanna pass this data to use this function*/ LocationsAPI.getLocations().then(resp =>
this.setState({ locations: resp, allLocations: resp })
);
}
render() {
console.log(this.state.locations);
return (
<div className="content">
<List/>
</div>
);
}
}
export default Content;
I've searched it too much. But it always gives errors. How can i pass discrict data for using Content component?
My understanding is that you want to pass district data from the Location component to it's ancestor the Content Component. Furthermore, you want the function that uses that data to run on Component Mount. I think one way of doing this would be to simply define a method inside of the Content Component that accepts the data as an argument. Then you can just pass that function down to the Location Component using the List Component. Since you have access to the function in the Location component, you can call it using your data. Since ComponentDidMount runs after all children have rendered, we can simply use a useEffect hook in the Location Component, and call our function just based on that hook.
It might look something like this:
class Content extends React.Component {
state = {
locations: [],
allLocations: [],
query: "",
};
LocationFunction(data){
console.log("DATA");
/*I wanna pass this data to use this function*/
LocationsAPI.getLocations().then(resp =>
this.setState({ locations: resp, allLocations: resp }));
}
render() {
console.log(this.state.locations);
return (
<div className="content">
<List LocationFunction={LocationFunction}/>
</div>
);
}
}
export default Content;
const Location = (props) => {
const [province, setProvince] = useState(null);
const [discrict, setDistrict] = useState(null);
const [discrictList, setdiscrictList] = useState([]);
useEffect(()=>{
props.LocationFunction(data);
},[district]);
const handleProvinceChange = (obj, e) => {
setProvince(obj);
setdiscrictList(obj.ilce);
setDistrict(null);
};
const handleDistrictChange = (obj) => {
setDistrict(obj);
};
return (
<div style={{ width: 400, marginBottom: 20 }}>
<Select
placeholder="Şehri Seçiniz"
value={province}
options={il}
onChange={handleProvinceChange}
getOptionLabel={x => x.il}
getOptionValue={x => x.il}
/>
<Select
placeholder="İlçeyi Seçiniz"
value={discrict}
options={discrictList}
onChange={handleDistrictChange}
getOptionLabel={x => x.ilce}
getOptionValue={x => x.il}
/>
</div>
)
}
export default Location;

React: save ref to state in a custom hook

I want to create a ref to an element, save it in state and use it somewhere else, down the line. Here is what I have so far:
const Header = () => {
const topElement = useRef();
const { setRootElement } = useScrollToTop();
useEffect(() => {
setRootElement(topElement);
}, []);
return (
<div ref={topElement}>
...
</div>
)
}
The useScrollToTop hook:
export const useScrollToTop = () => {
const [rootElement, setRootElement] = useState();
const scrollToTop = () => {
rootElement.current.scrollIntoView();
};
return {
scrollToTop: scrollToTop,
setRootElement: setRootElement
};
};
And in a different component:
const LongList = () => {
const { scrollToTop } = useScrollToTop();
return (
<div>
....
<button onClick={() => scrollToTop()} />
</div>
);
}
The setRootElemet works okay, it saves the element that I pass to it but when I call scrollToTop() the element is undefined. What am I missing here?
As hooks are essentially just functions, there is no state shared between calls. Each time you call useScrollToTop you are getting a new object with its own scrollToTop and setRootElement. When you call useScrollToTop in LongList, the returned setRootElement is never used and therefore that instance rootElement will never have a value.
What you need to do is have one call to useScrollToTop and pass the returned items to their respective components. Also, instead of using a state in the hook for the element, you can use a ref directly and return it.
Putting these together, assuming you have an App structure something like:
App
Header
LongList
Hook:
export const useScrollToTop = () => {
const rootElement = useRef();
const scrollToTop = () => {
rootElement.current.scrollIntoView();
};
return {
scrollToTop,
rootElement,
};
};
App:
...
const { scrollToTop, rootElement } = useScrollToTop();
return (
...
<Header rootElementRef={rootElement} />
<LongList scrollToTop={scrollToTop} />
...
);
Header:
const Header = ({ rootElementRef }) => {
return (
<div ref={rootElementRef}>
...
</div>
);
}
LongList:
const LongList = ({ scrollToTop }) => {
return (
<div>
...
<button onClick={() => scrollToTop()} />
</div>
);
}
The issue probably is topElement would be null initially and useEffect would trigger setRootElement with null. You would need to keep topElement in state variable and check when it changes and set the value inside your JSX as
const [topElement, setTopElement] = useState(null);
useEffect(() => {topElement && setRootElement(topElement);}, [topElement])
return (
<div ref={(ref) => setTopElement(ref)}>
...
</div>
);

React hooks useEffect calls mutiple times when redux store other data changed

my code like this:
Info component:
import {
getAttachData,
} from '#src/actions/creators/account'
const Info: React.FC = () => {
const info = useSelector<any, Account>(state => state.getIn(['account', 'info']).toJS())
const list = useSelector<any, Data[]>(state => state.getIn(['account', 'list']).toJS())
const attach = useSelector<any, AttachData[]>(state => state.getIn(['account', 'attach']).toJS())
...
const handleChange = ({ select }) => {
dispatch(getAttachData({v: select}))
}
const Template = (params) => {
return (
<div>
<BaseSelect onChange={(val) => handleChange(val)} list={list} />}
</div>
)
}
return (
...
<Template data={info} />
{attach.map((child, cidx) => (<Template data={child} />))}
)
}
export default Info
BaseSelect component:
const BaseSelect: React.FC<Props> = props => {
const [selectId, setSelectId] = useState('')
const { list } = props
useEffect(() => {
if (!isEmpty(list)) {
...
}
console.log('init')
}, [])
const handleChange = (value) => {
setSelectId(value)
props.onChange({
select: value,
})
}
return (
<Select
data={list}
value={selectId}
onChange={handleChange}
/>
)
}
export default BaseSelect
when excute handleChange event in BaseSelect component, the props.onChange function will call handleChange event in info component, and dispatch http request getAttachData which will change attach data in redux store, but useEffect in BaseSelect component will also excute and in console will print 'init' two times.
console:
It's because your Template component re-creates every time when redux store is changing.
Just move Template component outside the Info component.

React-Redux : re render child component on mapStateToProps in parent change doesn't work

I'm new to redux, and I'm trying to make a component reactive.
I want to re-render the MoveList component when the prop I'm passing down from parent mapStateToProps changes and it's not working.
I tried giving a key to the movelist component but it didn't work, and Im not sure how else to approach this
Parent component:
async componentDidMount() {
this.loadContact();
this.loadUser();
}
loadContact() {
const id = this.props.match.params.id;
this.props.loadContactById(id);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.match.params.id !== this.props.match.params.id) {
this.loadContact();
}
}
transferCoins = (amount) => {
const { contact } = this.props
console.log('amount', amount);
this.props.addMove(contact, amount)
console.log(this.props.user);
}
get filteredMoves() {
const moves = this.props.user.moves
return moves.filter(move => move.toId === this.props.contact._id)
}
render() {
const { user } = this.props;
const title = (contact) ? 'Your Moves:' : ''
if (!user) {
return <div> <img src={loadingSvg} /></div>;
}
return (
<div className="conact-deatils">
{ <MoveList className="move-list-cmp" title={title} moveList={this.props.user.moves} />}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
user: state.user.currUser
};
};
const mapDispatchToProps = {
loadContactById,
saveContact,
addMove
};
export default connect(mapStateToProps, mapDispatchToProps)(ContactDetailsPage);
Child component: moveList
export const MoveList = (props) => {
return (
<div className="moves-list">
<div className="title">{props.title}</div>
<hr/>
{props.moveList.map(move => {
return (
<ul className="move" key={move._id}>
{props.isFullList && <li>Name: {move.to}</li>}
<li>Amount: {move.amount}</li>
</ul>
)
})}
</div>
)
}
at the end the problem was that the parent component didn't re-render when i called the addMove dispatch. i didn't deep copied the array of moves object, and react don't know it need to re-render the component. i made a JSON.parse(JSON.stringify deep copy and the component.

Why render is not triggered even the props changed

I have been using react+redux quite while, but could you any one help me the following case, on codepen:
const {createStore } = Redux;
const { Provider, connect } = ReactRedux;
const store = createStore((state={name: 'ron'}, action) => {
switch(action.type) {
case 'changeName': return {name: action.name};
default: return state
}
})
const Person = props => {
const {name, dispatch} = props
console.log(`rendering Person due to name changed to ${name}`)
return (
<div>
<p> My name is {name} </p>
<button onClick={ () => dispatch({type: 'changeName', name: 'ron'}) } > Change to Ron </button>
<button onClick={ () => dispatch({type: 'changeName', name: 'john'}) } > Change to John</button>
</div>
)
}
const App = connect(state=>state)(Person)
ReactDOM.render(
<Provider store={store}><App/></Provider>,
document.getElementById('root')
);
It is simple react app, but I cannot explain:
Initialise redux store with one reducer, and its initValue is {name: 'ron'}
Click Change to ron button, it will dispatch {type: 'changeName', name: 'ron'}
When the reducer get this action, it will generate an brand new state {name: 'ron'}, though the value is same as the original state, but they are different identity and should be the different ones.
The functional component should be re-rendered if the props changed even though the values are the same. So I suppose the render function will be called, and console should output rendering Person due to.... However, it is not happening.
I am wondering why react functional component refuse to render again when the props identity are changed (though the values are the same)
Your connect(state=>state)(Person) I think it's not wrong but it's weird.
According to the documentation https://redux.js.org/docs/basics/UsageWithReact.html you can separate the state and the action dispatcher, commonly naming mapStateToProps and mapDispatchToProps.
So, I propose to you this code:
const mapStateToProps = state => ({
user: state.user
})
const mapDispatchToProps = dispatch => ({
updateName: (name) => dispatch(changeName(name)),
})
class DemoContainer extends Component {
constructor() {
super();
}
render() {
return (
<div>
<p> My name is {this.props.user.name}</p>
<button onClick={ () => this.props.updateName('ron') } > Change to Ron </button>
<button onClick={ () => this.props.updateName('john') } > Change to John</button>
</div>
);
}
}
const Demo = connect(
mapStateToProps,
mapDispatchToProps
)(DemoContainer)
export default Demo
My reducer:
const initialState = { name: 'John'}
const user = (state = initialState, action) => {
switch (action.type) {
case "CHANGE_NAME":
return {
name: action.name
}
default:
return state
}
}
export default user
My action:
export const changeName = ( name ) => ({
type: "CHANGE_NAME",
name,
})
You can check all my code here: https://stackblitz.com/edit/react-tchqrg
I have a class for the component but you can also use a functional component with connect like you do.

Resources