In my project, I actually use the context api. Additionally, I pass the name of an image from my component to the context provider on onclick, and then I take the name from the provider and pass it to another component. It is operating flawlessly. But there was a console error. When I send data to the context and access it in another component, the log always displays the same same props error.
TemplateLib:
<ImagesGrid
images={images}
getPreview={(image) => window.location.origin + '/' + image.image}
onSelect={async (image) => {
//here i'm passing the name to the provider function
await fetchEditData(image.name)
// localStorage.setItem('template_name', image.name)
const altered = JSON.parse(image.template_json)
store.loadJSON(altered);
}}
/>
Provider:
const App = ({ store }) => {
const [data, setData] = useState('')
const fetchEditData = async (name) => {
setData(await name)
}
return (
<PolotnoContext.Provider value={{ fetchEditData, data }} >
<div
style={{
width: '100%',
height: height + 'px',
display: 'flex',
flexDirection: 'column',
}}
>
// body
</div>
</PolotnoContext.Provider>
);
};
topBar.jsx:
// Here I'm getting the name
export default observer(({ store }) => {
const { data } = useContext(PolotnoContext)
console.log(data) // here same props error occur
return (
<>
<NavbarContainer className="bp4-navbar">
</NavbarContainer >
</>
);
});
Related
i'm using the tab view react native library to build a user account screen. my request is simple, how can i update the tab view content after an api call that fetches the user data?
function UserStoreScreen({ navigation, route }) {
const layout = useWindowDimensions();
const [index, setIndex] = React.useState(0);
const [userStore, setUserStore] = React.useState({});
const [routes] = React.useState([
{ key: "first", title: "Dressing" },
{ key: "second", title: "À propos" },
]);
const user = route.params;
// renders undefined
const FirstRoute = () => (
<>
<View style={styles.userContainer}>
<ListItem
image={`${IMAGES_BASE_URL}${userStore.photo}`}
title={`${userStore.username}`}
subTitle={`${userStore.store.length} Articles`}
/>
</View>
</>
);
const SecondRoute = () => (
<>
<View style={{ flex: 1, backgroundColor: "#ff4081" }} />
</>
);
const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});
const getUser = async () => {
await axiosApi
.post("/getUserProducts", { user_id: user.user_id })
.then((response) => {
// didn't work since set state is async
setUserStore(response.data);
})
.catch((err) => {
console.log(err);
});
};
// Get store products
useEffect(() => {
getUser();
}, []);
return (
<Screen style={styles.screen}>
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={{ width: layout.width }}
/>
</Screen>
);
}
is there a way to make the content of the tab view updated after i receive the data from the api call?
Yes, there is a way to forcefully re-mount a component. To do that, we can use key props like this:
return (
<Screen style={styles.screen}>
<TabView
key={JSON.stringify(userStore)}
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={{ width: layout.width }}
/>
</Screen>
);
How does key props work? Every time a component is re-rendering, it will check whether the key value is the same or not. If it's not the same, then force a component to re-render.
In this case we will always check if userStore value has changed or not.
I am trying to get configuration from default props in one typescript function in my React application, this is my first problem, usually if the file is JavaScript I can get props directly, but not same with typescript , I am using react portlet in Liferay 7.4 environment
Here is the component booking.tsx >
function Bookings() {
const agencyMemberId =
useArenaCoreConfig()?.getSelectedAgencyMemberSummary()?.id;
const [state, dispatch] = useReducer(reducer, initialState);
const {t} = useTranslation();
const showMoreHandler = () => {
dispatch({type: 'SET_PAGE'});
dispatch({type: 'SET_SIZE'});
};
const commonServicesConfig = useContext<CommonServicesConfig>(
CommonServicesConfigContext
);
const alertActionsRef =
useContext<React.RefObject<AlertActions>>(AlertActionsContext);
useEffect(() => {
try {
(async () => {
const response = await TransactionService.getConfirmedItems(
commonServicesConfig,
agencyMemberId,
state.page,
state.size
);
response.content.length > 20
? dispatch({payload: true, type: 'IS_SHOW_MORE'})
: dispatch({payload: false, type: 'IS_SHOW_MORE'});
dispatch({
payload: response.content
.sort((a, b) => +a.viewDate - +b.viewDate)
.slice(0, 20),
type: 'SET_CONFIRMED_TRANSACTION',
});
})();
} catch (error) {
alertActionsRef.current?.error(error);
}
// eslint-disable-next-line
}, [commonServicesConfig, state.page, state.size]);
return (
<React.Fragment>
<h4 className="mt-0">
{t('bookingsTitle')} ({state.confirmedTransactions.length})
</h4>
<div>
{state.confirmedTransactions.map((item) => (
<BookingItem
dispatch={dispatch}
item={item}
key={item.id}
transactionId={item.transactionId}
/>
))}
{state.isShowSeeMore && (
<ClayButton
displayType="secondary"
onClick={showMoreHandler}
style={{
display: 'block',
margin: 'auto',
marginBottom: 30,
}}
title={t('buttons.seeMore.label')}
>
{t('buttons.seeMore.title')}
</ClayButton>
)}
</div>
</React.Fragment>
);
}
export default Bookings;
I would like to use global props in useEffect(() anyone knows how I can pass it, I tried this.props and props directly but both give errors
https://liferay.dev/blogs/-/blogs/liferay-react-portlets
I have a functional component where I am using the MUI progress bar that I want to display but when the progress bar loads its still at the progress I set at the first step.
Also I am calling an API and processing the results in one of the functions. What am I doing wrong ?
function LinearProgressWithLabel(props: LinearProgressProps & { value: number }) {
return (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ width: '100%', mr: 1 }}>
<LinearProgress variant="determinate" {...props} />
</Box>
<Box sx={{ minWidth: 35 }}>
<Typography variant="body2" color="text.secondary">{`${Math.round(
props.value,
)}%`}</Typography>
</Box>
</Box>
);
}
export const Search = (props) => {
const { onSearchComplete } = props;
const [msgBox, setMsgBox] = useState(null);
const [loading, setLoading] = useState(false);
const [progress, setProgress] = useState(10);
const onSearch = async () => {
setLoading(true);
const emails = contacts
.filter(x => x.isChecked)
.map(item => item.emailAddress);
setProgress(30); //this is where I am manually setting the progress.
try {
const searchResults = await AppApi.search(emails);
let userList = [];
setProgress(70); // I want to manually set the percentage here
for (let i = 0; i < searchResults.length; i++) {
//processing the list here
}
onSearchComplete(userList); //passing on the results to another component
} catch (err) {
console.log({ err });
setMsgBox({ message: `${err.message}`, type: 'error' });
}
setLoading(false);
}
useEffect(() => {
onSearch();
}, [progress]);
return (
<Box>
{loading ? <LinearProgressWithLabel value={progress} />:
<Box>{msgBox && (<a style={{ cursor: 'pointer' }} onClick={() => setMsgBox(null)} title="Click to dismiss"><MessageBox type={msgBox.type || 'info'}>{msgBox.message}</MessageBox></a>)}</Box>}
</Box>
);
}
At the moment, your useEffect hook has the wrong dependencies. onSearch looks like it has two dependencies that could change - contacts and onSearchComplete, so the effect hook should actually be written as:
useEffect(() => {
onSearch();
}, [contacts, onSearchComplete]);
Depending on how onSearchComplete is defined, you might find that your effect re-runs more frequently than it should; you can either solve this by making onSearchComplete a callback:
const OtherComponent = () => {
const onSearchComplete = useCallback(userList => {
// ----- 8< -----
}, [...]);
}
Or wrapping the callback in a ref -
const Search = ({ onSearchComplete }) => {
const onSearchCompleteRef = useRef();
onSearchCompleteRef.current = onSearchComplete;
const onSearch = async () => {
// ----- 8< -----
onSearchCompleteRef.current(userList);
}
// Now you don't need onSearchComplete as a dependency
useEffect(() => {
onSearch();
}, [contacts]);
};
Edit
The reason you're not seeing the "updated" progress is because the processing of the results happens on the same render cycle as you updating the progress bar. The only way to get around that would be to introduce an artificial delay when you're processing the results:
setTimeout(() => {
onSearchCompleteRef.current();
setLoading(false);
}, 100);
I created a CodeSandbox demo to show this.
hello I'm using websocket to get my data, the problem is that when i tried to save the data into my state in react component is re rendering as expected but my data is not saved on state variable
export default function ResultPageContent() {
const processed = useSelector((state) => state.processedSlice);
const [data, setData] = useState({});
const fetchData = (links) => {
const socket = new webSocket("ws://localhost:5000/");
socket.onopen = () => {
socket.send(JSON.stringify(links));
};
socket.onmessage = ({ data }) => {
const tempData = JSON.parse(data);
setData(tempData);
};
socket.onclose = () => {};
};
const renderContent = () => {
if (data.per < 100) {
return <CircularProgressWithLabel value={data.per} />;
}
};
if (processed) {
console.log("links", links);
fetchData(links);
dispatch(MAKE_FALSE());
}
return (
<div>
<Paper
style={{
width: "100%",
background: theme.palette.background.default,
marginBottom: "50px",
}}
className={classes.root}
>
{renderContent()}
</Paper>
</div>
);
}```
I have a component called Tools which is accessed when I am on the route tools/:id. On this page, I have buttons with Links to this page, but it passes in a different id to the URL when clicked so that a different tool will be shown.
The getTool method just returns the correct component, and the component will only have one thing, an iFrame to show a calculator from another website.
So when I go back and forth between tools, the tools aren't loading until I click refresh. Otherwise, I get an error that says TypeError: Cannot read property 'style' of null. This is because I have document.getElementById('calc-SOME-NUMBER') but SOME_NUMBER is still referring to the last tool I was on. And this statement is within each tool and you can this below in BCalc.
I've checked the state, and when I go back and forth between tools, everything is correct; the correct tool is placed in the reducer. Any idea why this is happening? I was using history.go() as a workaround because I can't see any reason it still is lingering on to the old tools id.
const Tool = ({
getContent,
closeContent,
content: { content, loading, contents },
user,
match,
}) => {
const history = useHistory();
useEffect(() => {
scrollToTop();
getContent(match.params.id);
return () => {
logView(user, match.params.id, "tool");
closeContent();
history.go(); // doesn't work without this line
};
}, [getContent, closeContent, match.params.id, user]);
let ToolComponent;
const listItemStyle = { paddingTop: "40px", paddingBottom: "40px" };
return loading || content === null ? (
<Spinner />
) : (
<Container
style={{ marginTop: "3%" }}
fluid="true"
className="contentCardBody"
>
<Card>
<Card.Body>
{(ToolComponent = getTool(content.content_id.path))}
<ToolComponent />
</Card.Body>
</Card>
</Container>
);
};
Tool.propTypes = {
content: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
getContent: PropTypes.func.isRequired,
closeContent: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
content: state.content,
user: state.auth.user,
});
export default connect(mapStateToProps, {
getContent,
closeContent,
})(Tool);
Also, here is an example of what is returned from getTool():
const BCalc = () => {
const eventMethod = window.addEventListener ? 'addEventListener' : 'attachEvent';
const eventer = window[eventMethod];
const messageEvent = eventMethod === 'attachEvent' ? 'onmessage' : 'message';
eventer(
messageEvent,
(e) => {
if (e.origin === 'https://EXAMPLE.com') {
if (e.data.startsWith('height;')) {
document.getElementById('calc-SOME_NUMBER').style.height = `${e.data.split('height;')[1]}px`;
} else if (e.data.startsWith('url;')) {
window.location.href = e.data.split('url;')[1];
}
}
},
!1,
);
return (
<div>
<iframe
id="calc-SOME_NUMBER"
src="https://EXAMPLE_CALC"
title="tool"
scrolling="no"
style={{
width: '100%',
border: 'none',
marginTop: '-2%',
zIndex: '-1',
}}
/>
</div>
);
};
export default BCalc;