Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Answers -> Search 1: Customer facing renaming #141

Merged
merged 16 commits into from
Jul 14, 2022
89 changes: 47 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Search Headless React

Search Headless React is the official React UI Bindings layer for [Search Headless](https://www.npmjs.com/package/@yext/answers-headless).
Search Headless React is the official React UI Bindings layer for [Search Headless](https://www.npmjs.com/package/@yext/search-headless).

Written in 100% TypeScript.

Expand All @@ -25,7 +25,7 @@ npm install @yext/search-headless-react

## Getting Started - `SearchHeadlessProvider`

Search Headless React includes an `<SearchHeadlessProvider />` component, which instantiates an AnswersHeadless instance and makes it available to the rest of your app.
Search Headless React includes an `<SearchHeadlessProvider />` component, which instantiates an SearchHeadless instance and makes it available to the rest of your app.

```tsx
import { SearchHeadlessProvider } from '@yext/search-headless-react';
Expand All @@ -40,7 +40,7 @@ function MyApp() {
experienceKey='your experience key'
locale='en'
>
{/* Add components that use Answers as children */}
{/* Add components that use Search as children */}
<SearchBar/>
<MostRecentSearch/>
<UniversalResults/>
Expand All @@ -51,7 +51,7 @@ function MyApp() {

## Respond to State Updates with `useSearchState`

`useSearchState` reads a value from the `AnswersHeadless` state and subscribes to updates.
`useSearchState` reads a value from the `SearchHeadless` state and subscribes to updates.

```tsx
import { useSearchState } from '@yext/search-headless-react';
Expand All @@ -64,11 +64,11 @@ import { useSearchState } from '@yext/search-headless-react';

## Dispatch Actions with `useSearchActions`

`useSearchActions` allows you to dispatch actions using the `AnswersHeadless` instance.
`useSearchActions` allows you to dispatch actions using the `SearchHeadless` instance.

These include performing searches, getting autocomplete suggestions, and adding filters.

For a full list of capabilities see [the answers-headless docs](https://www.npmjs.com/package/@yext/answers-headless).
For a full list of capabilities see [the search-headless docs](https://www.npmjs.com/package/@yext/search-headless).

```tsx
import { useSearchActions } from '@yext/search-headless-react';
Expand All @@ -90,58 +90,63 @@ function SearchBar() {
}
```

## `AnswersHeadlessContext`
### Class Components
## Class Components

For users that want to use class components instead of functional components, you can use the `AnswersHeadlessContext` directly to dispatch actions and receive updates from state.
For users that want to use class components instead of functional components, you can use the `SearchHeadlessContext` directly to dispatch actions, and the `subscribeToStateUpdates` HOC to receive updates from state.

As an example, here is our simple SearchBar again, rewritten as a class using `AnswersHeadlessContext`.
These also work with functional components.

## `subscribeToStateUpdates`

Here is our MostRecentSearch component again, rewritten as a class with `subscribeToStateUpdates`.
oshi97 marked this conversation as resolved.
Show resolved Hide resolved

```tsx
import { AnswersHeadlessContext, AnswersHeadless, State } from '@yext/answers-headless-react';
import { subscribeToStateUpdates } from '@yext/search-headless-react';
import { Component } from 'react';

export default class Searcher extends Component {
static contextType = AnswersHeadlessContext;
unsubscribeQueryListener: any;
state = { query: "" };

componentDidMount() {
const answers: AnswersHeadless = this.context;
this.unsubscribeQueryListener = answers.addListener({
valueAccessor: (state: State) => state.query.mostRecentSearch,
callback: newPropsFromState => {
this.setState({ query: newPropsFromState })
}
});
}
interface Props {
mostRecentSearch: string
}

componentWillUnmount() {
this.unsubscribeQueryListener();
class MostRecentSearch extends Component<Props> {
render() {
return <div>Showing results for {this.props.mostRecentSearch}</div>;
}
}

export default subscribeToStateUpdates(state => ({
Copy link
Contributor

@yen-tt yen-tt Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want the updated README portion here from develop, I think you might have reverted back when resolving merge conflict (subscribeToStateUpdates is now deprecated)

mostRecentSearch: state.query.mostRecentSearch
}))(MostRecentSearch);
```

## `SearchHeadlessContext`

And here is our simple SearchBar again, rewritten as a class using `SearchHeadlessContext`.

```tsx
import { SearchHeadlessContext, SearchHeadless } from '@yext/search-headless-react';
import { Component } from 'react';

export default class Searcher extends Component {
static contextType = SearchHeadlessContext;

render() {
const answers: AnswersHeadless = this.context;
return (
<div>
<p>Query: {this.state.query}</p>
<input
onChange={evt => answers.setQuery(evt.target.value)}
onKeyDown={evt => {
if (evt.key === 'Enter') {
answers.executeUniversalQuery();
}
}}
/>
</div>
)
const search: SearchHeadless = this.context;
return <input
onChange={evt => search.setQuery(evt.target.value)}
onKeyDown={evt => {
if (evt.key === 'Enter') {
search.executeUniversalQuery();
}
}}
/>
}
}
```

## `useSearchUtilities`

We offer a `useSearchUtilities` convenience hook for accessing `AnswersHeadless.utilities`, which offers a number of stateless utility methods.
We offer a `useSearchUtilities` convenience hook for accessing `SearchHeadless.utilities`, which offers a number of stateless utility methods.
The `searchUtilities` and `searchUtilitiesFromActions` variables below are equivalent.

For class components, you can access `SearchUtilities` through `SearchHeadlessContext`.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@yext/search-headless-react",
"version": "1.2.0",
"description": "The official React UI Bindings layer for Answers Headless",
"description": "The official React UI Bindings layer for Search Headless",
"main": "./lib/esm/src/index.js",
"license": "BSD-3-Clause",
"types": "./lib/esm/src/index.d.ts",
Expand Down
10 changes: 5 additions & 5 deletions src/subscribeToStateUpdates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ type SubscriberGenerator = (WrappedComponent: ComponentType<any>) => (props: any

/**
* Generates a HOC that updates a given Component's props based on the current
* answers-headless state and a given mapping function.
* search-headless state and a given mapping function.
*
* @deprecated
* For class component, use `AnswersHeadlessContext` directly to dispatch actions and receive state updates.
* For functional component, use `useAnswersActions` and `useAnswersState` instead.
* For class component, use `SearchHeadlessContext` directly to dispatch actions and receive state updates.
* For functional component, use `useSearchActions` and `useSearchState` instead.
*/
export function subscribeToStateUpdates(
mapStateToProps: (s: State) => Record<string, unknown>
): SubscriberGenerator {
const generateSubscriberHOC: SubscriberGenerator = WrappedComponent => {
/**
* Keep manual track of the props mapped from state instead of storing
* it in the AnswersHeadlessSubscriber's state. This avoids react's batching
* it in the SearchHeadlessSubscriber's state. This avoids react's batching
* of state updates, which can result in mappedState not updating immediately.
* This can, in turn, result in extra answers-headless listener invocations.
* This can, in turn, result in extra search-headless listener invocations.
*/
let previousPropsFromState = {};
return function AnswersHeadlessSubscriber(props: Record<string, unknown>) {
Expand Down
4 changes: 2 additions & 2 deletions src/useSearchActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export type SearchActions = AnswersHeadless;
export function useSearchActions(): SearchActions {
const answersHeadless = useContext(SearchHeadlessContext);
if (answersHeadless.state === undefined) {
throw new Error('Attempted to call useAnswersActions() outside of SearchHeadlessProvider.'
+ ' Please ensure that \'useAnswersActions()\' is called within an SearchHeadlessProvider component.');
throw new Error('Attempted to call useSearchActions() outside of SearchHeadlessProvider.'
+ ' Please ensure that \'useSearchActions()\' is called within an SearchHeadlessProvider component.');
}
return answersHeadless;
}
2 changes: 1 addition & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@yext/answers-headless-react": ["src"]
"@yext/search-headless-react": ["src"]
},
"target": "es2015",
"esModuleInterop": true,
Expand Down