State not getting set using useState hook | ReactJS - reactjs

I have a component where I am storing API response in responseArray and I am using this responseArray to initialize matchFundResults state using hooks. Next, I am trying to run a useEffect using matchFundResults as a dependency but matchFundResults is always coming as blank whereas I have value in responseArray. How should this be fixed?
const MatchFundModal = ({ row, val }) => {
let rightBody;
const dispatch = useDispatch();
const selectedRows = useSelector((state) => state.pcPerformance.processor.checkedRows.selectedRows || []);
const responseArray = useSelector((state) => state.pcPerformance.processor.fundAliases);
const [showMatchFundModal, setshowMatchFundModal] = useState(val);
const [matchFundResults, setMatchFundResults] = useState(responseArray);
const [activeRowData, setActiveRowData] = useState({ Id: null, FundName: null, SourceId: null });
const [selectedMatchFund, setSelectedMatchFund] = useState();
const [searchFieldVal, setSearchFieldVal] = useState();
if (!activeRowData.Id) {
const firstRow = selectedRows.length > 0 && selectedRows[0];
setActiveRowData({ Id: firstRow.Id, FundName: firstRow.FundName, SourceId: firstRow.SourceId });
//dispatch(getFundNameAliasMatch(firstRow.FundName, firstRow.SourceId));
}
useEffect(() => {
dispatch(getFundNameAliasMatch(activeRowData.FundName, activeRowData.SourceId));
}, [activeRowData.Id]);
console.log('Helloworld responseArray', responseArray);
console.log('Helloworld matchFundResults', matchFundResults);
useEffect(() => {
rightBody = matchFundResults**.map((item) => {
return (
<li key={item.FundId}>
<input
type="radio"
value={item.FundId}
name="action-radio"
id={`action-radio-${item.FundId}-${item.SourceId}`}
onClick={(e) => handleRadioButtonClick(e)}
/>
<span>{item.FundName}</span>
<br />
<span className="searchFundID">#{item.FundId}</span>
</li>
);
});
}, [matchFundResults, activeRowData.Id]);
const matchFundBody = (
<div className="matchFundModal grid">
<p className="matchFundModal__header 12">Match or add</p>
<div className="matchFundModal__body 12">
<div className="matchFundModal__body__right 6">
<p id="possibleMatchText">Possible matches</p>
<ul>{rightBody}</ul>
</div>
</div>
<div className="matchFundModal__footer 12">
<button className="matchFundModal__footer__button">Match Selected</button>
</div>
</div>
);
return (
<Fragment>
<Modal
isOpen={showMatchFundModal}
bodyContent={matchFundBody}
showHeader={false}
handleOnModalToggleFunction={hideModal}
handleOnModalPrimaryButtonClick={onPrimaryButtonClick}
handleOnModalSecondaryButtonClick={hideModal}
primaryButtonText={'Match Fund'}
centered={true}
size="sm"
hideFooterButtons={true}
modalClasses="matchFundModal"
showFooter={false}
/>
</Fragment>
);
};
export default MatchFundModal;```
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/HxIv4.png

I don't know why you would want to copy responseArray to matchFundResults instead of just using responseArray directly but you never use setMatchFundResults when responseArray changes so you only set it initially and at that time responseArray is probably an empty array. You could do the following:
const responseArray = useSelector((state) =>
state.pcPerformance.processor.fundAliases);
const [matchFundResults, setMatchFundResults] = useState(responseArray);
//every time responseArray changes you need to set matchFundResults
useEffect(()=>setMatchFundResults(responseArray),[responseArray])
But it probably would be better to not copy redux state to local state and instead just use redux state directly.
Your comment suggest you have all data in redux state and would like to filter the data (the reason why you copy redux state to local state). You could do that with selectors in the following way:
const { Provider, useSelector } = ReactRedux;
const { createStore } = Redux;
const { createSelector } = Reselect;
const { useState, useMemo } = React;
const initialState = {
data: [
'hello world',
'hello redux',
'hello react',
'goodbye jquery',
],
};
const reducer = (state) => state;
//selectors
const selectData = (state) => state.data;
const createSelectFilteredData = (filter) =>
createSelector([selectData], (data) =>
data.filter((item) =>
item.toLowerCase().includes(filter.toLowerCase())
)
);
//creating store with redux dev tools
const store = createStore(reducer, initialState);
const App = () => {
const [filter, setFilter] = useState('');
const selectFilteredData = useMemo(
() => createSelectFilteredData(filter),
[filter]
);
const filteredData = useSelector(selectFilteredData);
return (
<div>
<label>
filter:
<input
type="text"
value={filter}
onChange={(e) => setFilter(e.target.value)}
/>
</label>
<div>
filtered data:
<pre>
{JSON.stringify(filteredData, undefined, 2)}
</pre>
</div>
</div>
);
};
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<div id="root"></div>

Related

Passing data between two components in React.js

Currently learning React and building a side project where i can render rss-feeds in my browser window. It works in a single component.
Original working component
function App (){
const [rssUrl, setRssUrl] = useState('');
const [items, setItems] = useState([]);
const getRss = async (e) => {
e.preventDefault();
const urlRegex =
/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,#?^=%&:\/~+#-]*[\w#?^=%&\/~+#-])?/;
if (!urlRegex.test(rssUrl)) {
return;
}
const res = await fetch(`https://api.allorigins.win/get?url=${rssUrl}`);
const { contents } = await res.json();
const feed = new window.DOMParser().parseFromString(contents, 'text/xml');
const items = feed.querySelectorAll('item');
const feedItems = [...items].map((el) => ({
link: el.querySelector('link').innerHTML,
title: el.querySelector('title').innerHTML,
author: el.querySelector('author').innerHTML,
}));
setItems(feedItems);
};
}
return (
<div className="App">
<form onSubmit={getRss}>
<div>
<h1>Next Pod For Chrome</h1>
<label> rss url</label>
<br />
<input onChange={(e) => setRssUrl(e.target.value)} value={rssUrl} />
</div>
<input type="submit" />
</form>
{items.map((item) => {
return (
<div>
<h1>{item.title}</h1>
<p>{item.author}</p>
<a href={item.link}>{item.link}</a>
</div>
);
})}
</div>
);
}
export default App;
At the moment I try to separate the functionality into two components. How can I pass a link from one component to another one where I want to trigger a function handled by the first component?
Any tips are much appreciated. Thanks.
Current state of component to search for rss-feed
function Search() {
const [rssUrl, setRssUrl] = useState('');
const formatRss = async (e) => {
e.preventDefault();
const urlRegex =
/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,#?^=%&:\/~+#-]*[\w#?^=%&\/~+#-])?/;
if (!urlRegex.test(rssUrl)) {
return;
}
console.log(rssUrl);
};
return (
<div className="App">
<form onSubmit={formatRss}>
<div>
<h1>Next Pod For Chrome</h1>
<label>rss url</label>
<br />
<input onChange={(e) => setRssUrl(e.target.value)} value={rssUrl} />
</div>
<input type="Submit" />
</form>
</div>
);
}
export default Search;
Current stage of component to parse and render
function List(props) {
const [items, setItems] = useState([]);
const formatRss = async (e) => {
e.preventDefault();
console.log(rssUrl);
const res = await fetch(`https://api.allorigins.win/get?url=${rssUrl}`);
const { contents } = await res.json();
const feed = new window.DOMParser().parseFromString(contents, 'text/xml');
const items = feed.querySelectorAll('item');
const feedItems = [...items].map((el) => ({
link: el.querySelector('link').innerHTML,
title: el.querySelector('title').innerHTML,
author: el.querySelector('author').innerHTML,
}));
setItems(feedItems);
};
return (
<div className="App">
{items.map((item) => {
return (
<div>
<h1>{item.title}</h1>
<p>{item.author}</p>
<a href={item.link}>{item.link}</a>
</div>
);
})}
</div>
);
}
export default List;
You can declare the state on both's parent, for example: App.js
And use prop to pass the variable to the component
like this:
export default function App() {
const [rssUrl, setRssUrl] = useState("");
return (
<div className="App">
<Search rssUrl={rssUrl} setRssUrl={setRssUrl} />
<List rssUrl={rssUrl} />
</div>
);
}
Below is the live example for you:
https://codesandbox.io/s/cocky-tharp-7d5uu8?file=/src/App.js
There are many platforms where you can put the demo project which make it easier for people to answer your question.

React: Check Array if string matches and set state

I have the following Array:
[
"/test/static/media/its9-odc_d.9d5de720.png",
"/test/static/media/its9-odc_m.178c1879.png",
"/test/static/media/its9-odc_w.5e70ca59.png",
"/test/static/media/its9-odc_y.8cf41473.png"
]
When I click on a Button, either _d, _m, _w or _y is saved in a React state (timeperiods).
I need a function which should save the string which matches my timeperiods in another React state (imageSource), so I can render the image.
This is what I tried so far but I always get returned 'yay' and when I try to setImageSource I get an error for infinite loop.
const imageForTimeperiod = () => {
images.forEach((img) => {
if (timeperiod && img.split('.')[0].includes(timeperiod)) {
console.log('yay');
// setImage(img);
}
})
};
{Object.entries(Timeperiods.timeperiods).map((entries) => {
return (
<Button onClick={() => setTimeperiod(entries[1].file)}>
{entries[0]}
</Button>
);
})}
In the end:
I click on Button 1 Day, it sets timeperiod to _d and I show the image /test/static/media/its9-odc_d.9d5de720.png.
You can pass the name of timeperiod on clicked button and then based on the name set the image and timePeriod state.
const { useState } = React;
const App = () => {
const [image, setImage] = useState(null);
const [timePeriod, setTimePeriod] = useState(null);
const images = [
"/test/static/media/its9-odc_d.9d5de720.png",
"/test/static/media/its9-odc_m.178c1879.png",
"/test/static/media/its9-odc_w.5e70ca59.png",
"/test/static/media/its9-odc_y.8cf41473.png"
];
const timePeriods = ['_d', '_m', '_w', '_y'];
const handleClick = (v) => {
const foundImage = images.find(name => name.includes(v));
setImage(foundImage || null);
setTimePeriod(v);
}
return <div>
{timePeriod}
<br />
{image && <img src={image} />}
<br />
{timePeriods.map((v, i) => {
return (
<button onClick={() => handleClick(v)}>
{v}
</button>
)
})}
</div>
}
ReactDOM.render(
<App />,
document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Can I render piece of a stateful component in react?

Is there any api that allow us to write code something like this:
const MyComponents = () => {
const [number, setNumber] = useState(0);
return {
Btn: <Button onPress={() => setNumber(number + 1)}>
{number}
</Button>,
Log: <p>{number}</p>
}
}
const Perent = () => <>
<div ...>
<MyComponents.Btn/>
...
...
</div>
<MyComponents.Log/>
</>
Some kind of ability to group some Component.And render them in different places...
Seems like this would be better achieved by using a Context.
E.g.
const { createContext, useState, useContext } = React;
const CountContext = createContext();
const CountContainer = ({ children }) => {
const [number, setNumber] = useState(0);
return <CountContext.Provider value={{ number, setNumber }}>
{children}
</CountContext.Provider>
};
const CountButton = () => {
const { number, setNumber } = useContext(CountContext);
return <button onClick={() => setNumber((c) => c + 1)}>
{number}
</button>;
};
const CountLog = () => {
const { number } = useContext(CountContext);
return <p>{number}</p>;
};
const SomeCountButtons = () => <div><CountButton /><CountButton /></div>;
const App = () => (<div>
<CountContainer>
<CountButton />
<CountLog />
</CountContainer>
<CountContainer>
<SomeCountButtons />
<CountLog />
</CountContainer>
</div>);
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Then any <CountButton>s or <CountLog>s that occur anywhere within the same <CountContainer> will be able to share their state.

Is there a way to make changes to the `window.location` object directly?

I am working on a submit handler where I'd like to navigate the user to /path?query, but I first found out that doing
window.location.pathname = `/path?query`
does not work as the ? is encoded.
I then tried
const handleSubmit = ({ email }: Values) => {
window.location = {
...window.location,
pathname: "/path",
search: `?email=${email}`,
};
};
But I got TypeError: Illegal invocation.
Here is a code snippet.
const {useState} = React;
const handleSubmitFactory = email => () => {
window.location = {
...window.location,
pathname: "path",
search: `?email=${email}`,
};
};
const handleChangeFactory = setEmail => ({ target: { value } }) => {
setEmail(value);
};
const Example = ({title}) => {
const [email, setEmail] = useState("");
return (
<form onSubmit={handleSubmitFactory(email)}>
<label>
Email:
<input type="email" value={email} onChange={handleChangeFactory(setEmail)} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
// Render it
ReactDOM.render(
<Example title="Example using Hooks:" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Is there a way to update the
window.location = `/path?query`
window.location.href = `/path?query`
This worked for me

Preview image upload with React Functions

hello im trying to make a image upload and preview but i can only find react tutorials on how to do this using class components
import React from 'react';
const AddNew = () => {
const fileHandler = (event) => {
console.log(event.target.files[0])
}
const alt = (event) => {
return(event.target.files[0].name)
}
const preview = (event) => {
return (
URL.createObjectURL(event.target.files[0])
)
}
return (
<div className="addNew">
<img src={preview} alt={alt}/>
<input type="file" onChange={fileHandler} />
</div>
)
}
export default AddNew
how do i preview it using this syntax?
i get an error for invalid values for props 'src' and 'alt'
You need to use state to let React know when to re-render. You can use useState hook to save your component state and file information, and when it changes, React knows it's the time to render.
const AddNew = ({}) => {
const [file, setFile] = React.useState(null)
const fileHandler = (e) => {
setFile(e.target.files[0])
}
return (
<div>
<img src={file? URL.createObjectURL(file) : null} alt={file? file.name : null}/>
<input type="file" onChange={fileHandler} />
</div>
)
}
ReactDOM.render(<AddNew />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"/>
Use a bit of state to provide an initial value for src and alt and hold the updated values.
const initialState = { alt: "", src: "" };
const AddNew = () => {
const [{ alt, src }, setPreview] = useState(initialState);
const fileHandler = event => {
const { files } = event.target;
setPreview(
files.length
? {
src: URL.createObjectURL(files[0]),
alt: files[0].name
}
: initialState
);
};
return (
<div className="addNew">
<img className="preview" src={src} alt={alt} />
<input accept="image/*" type="file" onChange={fileHandler} />
</div>
);
};
You need to use FileReader to convert the image to base64 string. Assuming you are doing a single file upload, just maintain a state for eg: file and set the state after the filereader onload listener is done reading image.
Working copy of your code is here:
import React, { useState } from "react";
const AddNew = () => {
const [file, setFile] = useState(null);
const fileHandler = event => {
console.log(event.target.files[0]);
let reader = new FileReader();
reader.onload = function(e) {
setFile(e.target.result);
};
reader.readAsDataURL(event.target.files[0]);
};
return (
<div className="addNew">
<img src={file} alt={""} />
<input type="file" onChange={fileHandler} />
</div>
);
};
export default AddNew;

Resources