const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
// ref.current就是<button>的对象
<FancyButton ref={ref}>Click me!</FancyButton>;
function Foo({bar, baz}) {
const options = {bar, baz}
React.useEffect(() => {
buzz(options)
}, [options]) // we want this to re-run if bar or baz change
return <div>foobar</div>
}
function Blub() {
return <Foo bar="bar value" baz={3} />
}
基础知识点
1. JSX
JSX是什么?
JSX是JavaScript的语法扩展,类似XML的描述方式,描述函数对象。而之所以不使用模板,是因为模板会分离技术栈,同时会引入更多的概念,就像Vue一样,会引入一些新的模板语法、模板指令等等,而JSX并不会引入新的概念,它就是一个JS。
为什么使用JSX?
JSX同时也是满足React的设计理念,即关注点分离。关注点分离是将计算机程序分隔为不同部分的设计原则,关注点分离使得解决特定领域问题的程序码从业务逻辑中独立出来,业务逻辑的程序码不再含有针对特定领域问题程序码的调用,业务逻辑同特定领域问题的关系通过侧面来封装、维护,当关注点分开时,各部分可以重复使用,独立开发和更新。
2. 类组件和函数组件
3. Props和Events
使用props从父组件传递到子组件,子组件可以使用props.name,props.clickFn()调用父组件的属性和方法
4. 生命周期
5. 条件编译
使用{}包裹着js相关的条件表达式进行组件的渲染
6. 列表渲染
7. 父子间通信
8. 路由跳转
高级知识点
1. 全局Context
(1) 创建useContext相关接口
interface AuthForm { username: string; password: string; }
const AuthContext = React.createContext<{ user: User|null, login: (form: AuthForm) => Promise,
register: (form: AuthForm) => Promise,
logout: ()=> Promise
} | undefined>(undefined);
AuthContext.displayName = "AuthContext";
export const AuthProvider = ({children}: { children: ReactNode }) => { const [user, setUser] = useState<User | null>(null);
}
// 提供给子组件使用的方法:类似与Vue的inject export const useAuth = () => { const context = React.useContext(AuthContext); if (!context) { throw new Error("useAuth必须在AuthProvider中使用"); } return context; }
(3) 在需要使用全局方法的地方使用useAuth
2. Refs绑定Dom对象
(1) React.forwardRef
封装ref的传递,实现ref的向下传递,FancyButton可以获取父组件传来的ref然后赋值到
<button>
中3. 状态提升
使用React经常会遇到几个组件需要共用状态数据的情况,在这种情况下,我们最好将这部分共享的状态提升至他们最近的父组件当中进行管理。这样所有子组件的数据都是来自他们最近的父组件,由父组件进行统一存储和修改,然后传入到子组件中。
4. redux或者Mobx(非useContext和状态提升的另一种全局状态管理)
(1) redux
概念
跟Vuex类似,使用叫做"action"的事件来管理和更新应用状态的模式和工具库。它以集中式Store的方式对整个应用中使用的状态进行集中管理,其规则保证状态只能以可预测的方式更新。
其它工具包
React-Redux#
Redux 可以集成到任何的 UI 框架中,其中最常见的是 React 。React-Redux 是我们的官方包,它可以让 React 组件访问 state 和下发 action 更新 store,从而同 Redux 集成起来。
Redux Toolkit#
Redux Toolkit 是我们推荐的编写 Redux 逻辑的方法。 它包含我们认为对于构建 Redux 应用程序必不可少的包和函数。 Redux Toolkit 构建在我们建议的最佳实践中,简化了大多数 Redux 任务,防止了常见错误,并使编写 Redux 应用程序变得更加容易。
Redux DevTools 扩展#
Redux DevTools 扩展 可以显示 Redux 存储中状态随时间变化的历史记录。这允许您有效地调试应用程序,包括使用强大的技术,如“时间旅行调试”。
使用示例
const initialState = [ { id: '1', title: 'First Post!', content: 'Hello!' }, { id: '2', title: 'Second Post', content: 'More text' } ]
const postsSlice = createSlice({ name: 'posts', initialState, reducers: {} })
export default postsSlice.reducer;
// 我们也可以在createSlice中创建function,useSelector直接调用,比如
// slice.js export const selectAllPosts = state => state.posts; export const selectPostById = (state, postId) => state.posts.find(post => post.id === postId);
// 调用的地方 const posts = useSelector(selectAllPosts) const post = useSelector(state => selectPostById(state, postId))
从下面的例子可以看出,useDispatch调用跟普通的actions没有什么区别,区别在于createSlice的时候要使用createAsyncThunk进行显示声明
import { client } from '../../api/client'
export const fetchNotifications = createAsyncThunk( 'notifications/fetchNotifications', async (_, { getState }) => { const allNotifications = selectAllNotifications(getState()) const [latestNotification] = allNotifications const latestTimestamp = latestNotification ? latestNotification.date : '' const response = await client.get(
/fakeApi/notifications?since=${latestTimestamp}
) return response.notifications } )const notificationsSlice = createSlice({ name: 'notifications', initialState: [], reducers: {}, extraReducers: { [fetchNotifications.fulfilled]: (state, action) => { state.push(...action.payload) // Sort with newest first state.sort((a, b) => b.date.localeCompare(a.date)) } } })
export default notificationsSlice.reducer
export const selectAllNotifications = state => state.notifications
useState也支持异步初始化值,如果初始化的值需要进行复杂的计算,那么可以使用函数进行初始化,后续的setState更新会忽略该function的执行,useState会在整个DOM渲染时只渲染一次,因此这个初始化function也只会执行一次
2. useEffect
3. useContext
创建全局的对象,类似于Vue的inject和provide,可以看高级知识点的1.全局Context
4. useMemo和useCallback
(1) 使用上面两个hook方法的原因:对象不断重新创建
这里有问题的原因是因为 useEffect 将对每次渲染中对 options 进行引用相等性检查,并且由于JavaScript的工作方式,每次渲染 options 都是新的,所以当React测试 options 是否在渲染之间发生变化时,它将始终计算为 true,意味着每次渲染后都会调用 useEffect 回调,而不是仅在 bar 和 baz 更改时调用。 针对上面的代码,我们做的改进是
使用了React.useCallback和React.useMemo后,我们每次重新渲染Blub()的时候,就不会重新建立bar和baz了,因此也不会触发useEffect里面方法的重新执行
(2) 使用上面两个hook方法的原因:昂贵的计算
针对上面的代码,我们做的改进是
使用了useMemo后,每次重新渲染RenderPrimes时就不会重新创建primes对象,也不会重新执行一遍 耗时的calculatePrimes()方法
6. useRef和createRef
(1) 不同点
useRef创建的对象在每一次组件重新渲染时都不会重新创建,一直保持着原有对象的引用 createRef创建的对象在每一次组件重新渲染时都会重新执行一次,重新创建一个新的ref对象
(2) 相同点
用于对子组件的引用
7. useSearchParams以及常见用法
// TODO
常见的第三方库
1. react-query
对网络请求进行封装的一个库,这个库将帮助你获取、同步、更新和缓存你的远程数据,提供简单的hooks,就能完成增删查改等操作。我们有了react-query,就不用使用useReduce,繁杂的配置,维护全局状态,只要知道如何使用Promise,传递一个可解析的函数即可。
与useSearchParams结合的常见用法
// TODO